#ifndef __SW_INTERNAL_H__
#define __SW_INTERNAL_H__


//-----------------------------------------------------------------------------
// [SECTION] Header mess
//-----------------------------------------------------------------------------

#ifndef SW_VERSION
#include "sweety.h"
#endif

#include <stdio.h>      // FILE*, sscanf
#include <stdlib.h>     // NULL, malloc, free, qsort, atoi, atof
#include <math.h>       // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
#include <limits.h>     // INT_MIN, INT_MAX

// Enable SSE intrinsics if available
#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(SW_DISABLE_SSE)
#define SW_ENABLE_SSE
#include <immintrin.h>
#endif

// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 4251)                                     // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when SW_API is set to__declspec(dllexport)
#pragma warning (disable: 26812)                                    // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26495)                                    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#if defined(_MSC_VER) && _MSC_VER >= 1922                           // MSVC 2019 16.2 or later
#pragma warning (disable: 5054)                                     // operator '|': deprecated between enumerations of different types
#endif
#endif

// Clang/GCC warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#if __has_warning("-Wunknown-warning-option")
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for SwFloorSigned()
#pragma clang diagnostic ignored "-Wunused-function"                // for stb_textedit.h
#pragma clang diagnostic ignored "-Wmissing-prototypes"             // for stb_textedit.h
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wmissing-noreturn"               // warning: function 'xxx' could be declared with attribute 'noreturn'
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif

// Legacy defines
#ifdef SW_DISABLE_FORMAT_STRING_FUNCTIONS                      // Renamed in 1.74
#error Use SW_DISABLE_FORMAT_STRING_FUNCTIONS
#endif
#ifdef SW_DISABLE_MATH_FUNCTIONS                               // Renamed in 1.74
#error Use SW_DISABLE_MATH_FUNCTIONS
#endif

// Enable stb_truetype by default unless FreeType is enabled.
// You can compile with both by defining both SW_ENABLE_FREETYPE and SW_ENABLE_STB_TRUETYPE together.
#ifndef SW_ENABLE_FREETYPE
#define SW_ENABLE_STB_TRUETYPE
#endif


//-----------------------------------------------------------------------------
// [SECTION] Forward declarations
//-----------------------------------------------------------------------------

struct SwBitVector;                 // Store 1-bit per value
struct SwRect;                      // An axis-aligned rectangle (2 points)
struct SwDrawDataBuilder;           // Helper to build a SwDrawData instance
struct SwDrawListSharedData;        // Data shared between all SwDrawList instances
struct SwGuiColorMod;               // Stacked color modifier, backup of modified data so we can restore it
struct SwGuiContext;                // Main  SwGui context
struct SwGuiContextHook;            // Hook for extensions like SwGuiTestEngine
struct SwGuiDataTypeInfo;           // Type information associated to a SwGuiDataType enum
struct SwGuiGroupData;              // Stacked storage data for BeginGroup()/EndGroup()
struct SwGuiDockContext;            // Docking system context
struct SwGuiDockRequest;            // Docking system dock/undock queued request
struct SwGuiDockNode;               // Docking system node (hold a list of Windows OR two child dock nodes)
struct SwGuiDockNodeSettings;       // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session)
struct SwGuiInputTextState;         // Internal state of the currently focused/edited text input box
struct SwGuiLastItemData;           // Status storage for last submitted items
struct SwGuiMenuColumns;            // Simple column measurement, currently used for MenuItem() only
struct SwGuiNavItemData;            // Result of a gamepad/keyboard directional navigation move query result
struct SwGuiMetricsConfig;          // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
struct SwGuiNextWindowData;         // Storage for SetNextWindow** functions
struct SwGuiNextItemData;           // Storage for SetNextItem** functions
struct SwGuiOldColumnData;          // Storage data for a single column for legacy Columns() api
struct SwGuiOldColumns;             // Storage data for a columns set for legacy Columns() api
struct SwGuiPopupData;              // Storage for current popup stack
struct SwGuiSettingsHandler;        // Storage for one type registered in the .ini file
struct SwGuiStackSizes;             // Storage of stack sizes for debugging/asserting
struct SwGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it
struct SwGuiTabBar;                 // Storage for a tab bar
struct SwGuiTabItem;                // Storage for a tab item (within a tab bar)
struct SwGuiTable;                  // Storage for a table
struct SwGuiTableColumn;            // Storage for one column of a table
struct SwGuiTableInstanceData;      // Storage for one instance of a same table
struct SwGuiTableTempData;          // Temporary storage for one table (one per table in the stack), shared between tables.
struct SwGuiTableSettings;          // Storage for a table .ini settings
struct SwGuiTableColumnsSettings;   // Storage for a column .ini settings
struct SwGuiWindow;                 // Storage for one window
struct SwGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)
struct SwGuiWindowSettings;         // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)

// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
typedef int SwGuiDataAuthority;         // -> enum SwGuiDataAuthority_      // Enum: for storing the source authority (dock node vs window) of a field
typedef int SwGuiLayoutType;            // -> enum SwGuiLayoutType_         // Enum: Horizontal or vertical
typedef int SwGuiActivateFlags;         // -> enum SwGuiActivateFlags_      // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int SwGuiDebugLogFlags;         // -> enum SwGuiDebugLogFlags_      // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int SwGuiFocusRequestFlags;     // -> enum SwGuiFocusRequestFlags_  // Flags: for FocusWindow();
typedef int SwGuiInputFlags;            // -> enum SwGuiInputFlags_         // Flags: for IsKeyPressed()
typedef int SwGuiItemFlags;             // -> enum SwGuiItemFlags_          // Flags: for PushItemFlag()
typedef int SwGuiItemStatusFlags;       // -> enum SwGuiItemStatusFlags_    // Flags: for DC.LastItemStatusFlags
typedef int SwGuiOldColumnFlags;        // -> enum SwGuiOldColumnFlags_     // Flags: for BeginColumns()
typedef int SwGuiNavHighlightFlags;     // -> enum SwGuiNavHighlightFlags_  // Flags: for RenderNavHighlight()
typedef int SwGuiNavMoveFlags;          // -> enum SwGuiNavMoveFlags_       // Flags: for navigation requests
typedef int SwGuiNextItemDataFlags;     // -> enum SwGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions
typedef int SwGuiNextWindowDataFlags;   // -> enum SwGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions
typedef int SwGuiScrollFlags;           // -> enum SwGuiScrollFlags_        // Flags: for ScrollToItem() and navigation requests
typedef int SwGuiSeparatorFlags;        // -> enum SwGuiSeparatorFlags_     // Flags: for SeparatorEx()
typedef int SwGuiTextFlags;             // -> enum SwGuiTextFlags_          // Flags: for TextEx()
typedef int SwGuiTooltipFlags;          // -> enum SwGuiTooltipFlags_       // Flags: for BeginTooltipEx()

typedef void (*SwGuiErrorLogCallback)(void* user_data, const char* fmt, ...);


//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
//-----------------------------------------------------------------------------

#ifndef GMUI_Ctx
extern SW_API SwGuiContext* GMUI_Ctx;  // Current implicit context pointer
#endif


//-------------------------------------------------------------------------
// [SECTION] STB libraries includes
//-------------------------------------------------------------------------

namespace SwStb
{

#undef STB_TEXTEDIT_STRING
#undef STB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_STRING             SwGuiInputTextState
#define STB_TEXTEDIT_CHARTYPE           SwWchar
#define STB_TEXTEDIT_GETWIDTH_NEWLINE   (-1.0f)
#define STB_TEXTEDIT_UNDOSTATECOUNT     99
#define STB_TEXTEDIT_UNDOCHARCOUNT      999
#include "stb/stb_textedit.h"

} // namespace ImStb

//-----------------------------------------------------------------------------
// [SECTION] Macros
//-----------------------------------------------------------------------------

// Internal Drag and Drop payload types. String starting with '_' are reserved for  SwGui.
#define SW_PAYLOAD_TYPE_WINDOW       "_IMWINDOW"     // Payload == SwGuiWindow*

// Debug Printing Into TTY
// (since SW_VERSION_NUM >= 18729: SW_DEBUG_LOG was reworked into SW_DEBUG_PRINTF (and removed framecount from it). If you were using a #define SW_DEBUG_LOG please rename)
#ifndef SW_DEBUG_PRINTF
#ifndef SW_DISABLE_DEFAULT_FORMAT_FUNCTIONS
#define SW_DEBUG_PRINTF(_FMT,...)    printf(_FMT, __VA_ARGS__)
#else
#define SW_DEBUG_PRINTF(_FMT,...)
#endif
#endif

// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.
#define SW_DEBUG_LOG(...)            SW::DebugLog(__VA_ARGS__);
#define SW_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventActiveId) SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventFocus)    SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_POPUP(...)      do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventPopup)    SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_NAV(...)        do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventNav)      SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_CLIPPER(...)    do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventClipper)  SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_IO(...)         do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventIO)       SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_DOCKING(...)    do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventDocking)  SW_DEBUG_LOG(__VA_ARGS__); } while (0)
#define SW_DEBUG_LOG_VIEWPORT(...)   do { if (g.DebugLogFlags & SwGuiDebugLogFlags_EventViewport) SW_DEBUG_LOG(__VA_ARGS__); } while (0)

// Static Asserts
#define SW_STATIC_ASSERT(_COND)         static_assert(_COND, "")

// "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much.
// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code.
//#define SW_DEBUG_PARANOID
#ifdef SW_DEBUG_PARANOID
#define SW_ASSERT_PARANOID(_EXPR)       SW_ASSERT(_EXPR)
#else
#define SW_ASSERT_PARANOID(_EXPR)
#endif

// Error handling
// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
#ifndef SW_ASSERT_USER_ERROR
#define SW_ASSERT_USER_ERROR(_EXP,_MSG) SW_ASSERT((_EXP) && _MSG)   // Recoverable User Error
#endif

// Misc Macros
#define SW_PI                           3.14159265358979323846f
#ifdef _WIN32
#define SW_NEWLINE                      "\r\n"   // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!)
#else
#define SW_NEWLINE                      "\n"
#endif
#ifndef SW_TABSIZE                      // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override
#define SW_TABSIZE                      (4)
#endif
#define SW_MEMALIGN(_OFF,_ALIGN)        (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))           // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8
#define SW_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose
#define SW_F32_TO_INT8_SAT(_VAL)        ((int)(SwSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
#define SW_TRUNC(_VAL)                  ((float)(int)(_VAL))                                    // SwTrunc() is not inlined in MSVC debug builds
#define SW_ROUND(_VAL)                  ((float)(int)((_VAL) + 0.5f))                           //
#define SW_STRINGIFY_HELPER(_X)         #_X
#define SW_STRINGIFY(_X)                SW_STRINGIFY_HELPER(_X)                                 // Preprocessor idiom to stringify e.g. an integer.
#ifndef SW_DISABLE_OBSOLETE_FUNCTIONS
#define SW_FLOOR SW_TRUNC
#endif

// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
#ifdef _MSC_VER
#define SW_CDECL __cdecl
#else
#define SW_CDECL
#endif

// Warnings
#if defined(_MSC_VER) && !defined(__clang__)
#define SW_MSVC_WARNING_SUPPRESS(XXXX)  __pragma(warning(suppress: XXXX))
#else
#define SW_MSVC_WARNING_SUPPRESS(XXXX)
#endif

// Debug Tools
// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item.
// This will call SW_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference.
#ifndef SW_DEBUG_BREAK
#if defined (_MSC_VER)
#define SW_DEBUG_BREAK()    __debugbreak()
#elif defined(__clang__)
#define SW_DEBUG_BREAK()    __builtin_debugtrap()
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
#define SW_DEBUG_BREAK()    __asm__ volatile("int $0x03")
#elif defined(__GNUC__) && defined(__thumb__)
#define SW_DEBUG_BREAK()    __asm__ volatile(".inst 0xde01")
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
#define SW_DEBUG_BREAK()    __asm__ volatile(".inst 0xe7f001f0");
#else
#define SW_DEBUG_BREAK()    SW_ASSERT(0)    // It is expected that you define SW_DEBUG_BREAK() into something that will break nicely in a debugger!
#endif
#endif // #ifndef SW_DEBUG_BREAK

//-----------------------------------------------------------------------------
// [SECTION] Generic helpers
// Note that the SwXXX helpers functions are lower-level than SwGui functions.
// SwGui functions or the SwGui context are never called/used from other SwXXX functions.
//-----------------------------------------------------------------------------
// - Helpers: Hashing
// - Helpers: Sorting
// - Helpers: Bit manipulation
// - Helpers: String
// - Helpers: Formatting
// - Helpers: UTF-8 <> wchar conversions
// - Helpers: SwVec2/SwVec4 operators
// - Helpers: Maths
// - Helpers: Geometry
// - Helper: SwVec1
// - Helper: SwVec2ih
// - Helper: SwRect
// - Helper: SwBitArray
// - Helper: SwBitVector
// - Helper: SwSpan<>, SwSpanAllocator<>
// - Helper: SwPool<>
// - Helper: SwChunkStream<>
//-----------------------------------------------------------------------------


// Helpers: Hashing
SW_API SwGuiID              SwHashData(const void* data, size_t data_size, SwU32 seed = 0);
SW_API SwGuiID              SwHashStr(const char* data, size_t data_size = 0, SwU32 seed = 0);

// Helpers: Sorting
#ifndef SwQsort
static inline void          SwQsort(void* base, size_t count, size_t size_of_element, int(SW_CDECL * compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }
#endif

// Helpers: Color Blending
SW_API SwU32                SwAlphaBlendColors(SwU32 col_a, SwU32 col_b);

// Helpers: Bit manipulation
static inline bool          SwIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
static inline bool          SwIsPowerOfTwo(SwU64 v) { return v != 0 && (v & (v - 1)) == 0; }
static inline int           SwUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }

// Helpers: String
SW_API int                  SwStricmp(const char* str1, const char* str2);
SW_API int                  SwStrnicmp(const char* str1, const char* str2, size_t count);
SW_API void                 SwStrncpy(char* dst, const char* src, size_t count);
SW_API char*                SwStrdup(const char* str);
SW_API char*                SwStrdupcpy(char* dst, size_t* p_dst_size, const char* str);
SW_API const char*          SwStrchrRange(const char* str_begin, const char* str_end, char c);
SW_API int                  SwStrlenW(const SwWchar* str);
SW_API const char*          SwStreolRange(const char* str, const char* str_end);                // End end-of-line
SW_API const SwWchar*       SwStrbolW(const SwWchar* buf_mid_line, const SwWchar* buf_begin);   // Find beginning-of-line
SW_API const char*          SwStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
SW_API void                 SwStrTrimBlanks(char* str);
SW_API const char*          SwStrSkipBlank(const char* str);
static inline bool          SwCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool          SwCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }

// Helpers: Formatting
SW_API int                  SwFormatString(char* buf, size_t buf_size, const char* fmt, ...) SW_FMTARGS(3);
SW_API int                  SwFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) SW_FMTLIST(3);
SW_API void                 SwFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) SW_FMTARGS(3);
SW_API void                 SwFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) SW_FMTLIST(3);
SW_API const char*          SwParseFormatFindStart(const char* format);
SW_API const char*          SwParseFormatFindEnd(const char* format);
SW_API const char*          SwParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
SW_API void                 SwParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
SW_API const char*          SwParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
SW_API int                  SwParseFormatPrecision(const char* format, int default_value);

// Helpers: UTF-8 <> wchar conversions
SW_API const char*          SwTextCharToUtf8(char out_buf[5], unsigned int c);                                                      // return out_buf
SW_API int                  SwTextStrToUtf8(char* out_buf, int out_buf_size, const SwWchar* in_text, const SwWchar* in_text_end);   // return output UTF-8 bytes count
SW_API int                  SwTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);               // read one character. return input UTF-8 bytes count
SW_API int                  SwTextStrFromUtf8(SwWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count
SW_API int                  SwTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                                 // return number of UTF-8 code-points (NOT bytes count)
SW_API int                  SwTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8
SW_API int                  SwTextCountUtf8BytesFromStr(const SwWchar* in_text, const SwWchar* in_text_end);

// Helpers: SwVec2/SwVec4 operators
// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between SwVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.)
// We unfortunately don't have a unary- operator for SwVec2 because this would needs to be defined inside the class itself.
#ifdef SW_DEFINE_MATH_OPERATORS
SW_MSVC_RUNTIME_CHECKS_OFF
static inline SwVec2 operator*(const SwVec2& lhs, const float rhs) { return     SwVec2(lhs.x * rhs, lhs.y * rhs); }
static inline SwVec2 operator/(const SwVec2& lhs, const float rhs) { return     SwVec2(lhs.x / rhs, lhs.y / rhs); }
static inline SwVec2 operator+(const SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
static inline SwVec2 operator-(const SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x - rhs.x, lhs.y - rhs.y); }
static inline SwVec2 operator*(const SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline SwVec2 operator/(const SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x / rhs.x, lhs.y / rhs.y); }
static inline SwVec2& operator*=(SwVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
static inline SwVec2& operator/=(SwVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
static inline SwVec2& operator+=(SwVec2& lhs, const SwVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
static inline SwVec2& operator-=(SwVec2& lhs, const SwVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
static inline SwVec2& operator*=(SwVec2& lhs, const SwVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }
static inline SwVec2& operator/=(SwVec2& lhs, const SwVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
static inline SwVec4 operator+(const SwVec4& lhs, const SwVec4& rhs) { return SwVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
static inline SwVec4 operator-(const SwVec4& lhs, const SwVec4& rhs) { return SwVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
static inline SwVec4 operator*(const SwVec4& lhs, const SwVec4& rhs) { return SwVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
SW_MSVC_RUNTIME_CHECKS_RESTORE
#endif

// Helpers: File System
#ifdef SW_DISABLE_FILE_FUNCTIONS
#define SW_DISABLE_DEFAULT_FILE_FUNCTIONS
typedef void* SwFileHandle;
static inline SwFileHandle  SwFileOpen(const char*, const char*) { return NULL; }
static inline bool            SwFileClose(SwFileHandle) { return false; }
static inline SwU64         SwFileGetSize(SwFileHandle) { return (SwU64)-1; }
static inline SwU64         SwFileRead(void*, SwU64, SwU64, SwFileHandle) { return 0; }
static inline SwU64         SwFileWrite(const void*, SwU64, SwU64, SwFileHandle) { return 0; }
#endif
#ifndef SW_DISABLE_DEFAULT_FILE_FUNCTIONS
typedef FILE* SwFileHandle;
SW_API SwFileHandle         SwFileOpen(const char* filename, const char* mode);
SW_API bool                 SwFileClose(SwFileHandle file);
SW_API SwU64                SwFileGetSize(SwFileHandle file);
SW_API SwU64                SwFileRead(void* data, SwU64 size, SwU64 count, SwFileHandle file);
SW_API SwU64                SwFileWrite(const void* data, SwU64 size, SwU64 count, SwFileHandle file);
#else
#define SW_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions
#endif
SW_API void*                SwFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0);

// Helpers: Maths
SW_MSVC_RUNTIME_CHECKS_OFF
// - Wrapper for standard libs functions. (Note that mgui_demo.cpp does _not_ use them to keep the code easy to copy)
#ifndef SW_DISABLE_DEFAULT_MATH_FUNCTIONS
#define SwFabs(X)           fabsf(X)
#define SwSqrt(X)           sqrtf(X)
#define SwFmod(X, Y)        fmodf((X), (Y))
#define SwCos(X)            cosf(X)
#define SwSin(X)            sinf(X)
#define SwAcos(X)           acosf(X)
#define SwAtan2(Y, X)       atan2f((Y), (X))
#define SwAtof(STR)         atof(STR)
//#define SwFloorStd(X)     floorf(X)           // We use our own, see SwFloor() and SwFloorSigned()
#define SwCeil(X)           ceilf(X)
static inline float           SwPow(float x, float y) { return powf(x, y); }          // DragBehaviorT/SliderBehaviorT uses SwPow with either float/double and need the precision
static inline double          SwPow(double x, double y) { return pow(x, y); }
static inline float           SwLog(float x) { return logf(x); }             // DragBehaviorT/SliderBehaviorT uses SwLog with either float/double and need the precision
static inline double          SwLog(double x) { return log(x); }
static inline int             SwAbs(int x) { return x < 0 ? -x : x; }
static inline float           SwAbs(float x) { return fabsf(x); }
static inline double          SwAbs(double x) { return fabs(x); }
static inline float           SwSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument
static inline double          SwSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }
#ifdef SW_ENABLE_SSE
static inline float           SwRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }
#else
static inline float           SwRsqrt(float x) { return 1.0f / sqrtf(x); }
#endif
static inline double          SwRsqrt(double x) { return 1.0 / sqrt(x); }
#endif
// - SwMin/SwMax/SwClamp/SwLerp/SwSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
// (Exceptionally using templates here but we could also redefine them for those types)
template<typename T> static inline T    SwMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; }
template<typename T> static inline T    SwMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; }
template<typename T> static inline T    SwClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
template<typename T> static inline T    SwLerp(T a, T b, float t) { return (T)(a + (b - a) * t); }
template<typename T> static inline void SwSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
template<typename T> static inline T    SwAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
template<typename T> static inline T    SwSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
// - Misc maths helpers
static inline SwVec2    SwMin(const    SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }
static inline SwVec2    SwMax(const    SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }
static inline SwVec2    SwClamp(const  SwVec2& v,   const SwVec2& mn, SwVec2 mx) { return SwVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }
static inline SwVec2    SwLerp(const   SwVec2& a,   const SwVec2& b, float t) { return SwVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }
static inline SwVec2    SwLerp(const   SwVec2& a,   const SwVec2& b, const SwVec2& t) { return SwVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
static inline SwVec4    SwLerp(const   SwVec4& a,   const SwVec4& b, float t) { return SwVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }
static inline float     SwSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
static inline float     SwLengthSqr(const SwVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
static inline float     SwLengthSqr(const SwVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
static inline float     SwInvLength(const SwVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return SwRsqrt(d); return fail_value; }
static inline float     SwTrunc(float f) { return (float)(int)(f); }
static inline SwVec2    SwTrunc(const SwVec2& v) { return SwVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline float     SwFloor(float f) { return (float)(int)(f); }
static inline float     SwFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline SwVec2    SwFloor(const SwVec2& v) { return SwVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline SwVec2    SwFloorSigned(const SwVec2& v) { return SwVec2(SwFloorSigned(v.x), SwFloorSigned(v.y)); }
static inline int       SwModPositive(int a, int b) { return (a + b) % b; }
static inline float     SwDot(const SwVec2& a, const SwVec2& b) { return a.x * b.x + a.y * b.y; }
static inline SwVec2    SwRotate(const SwVec2& v, float cos_a, float sin_a) { return SwVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
static inline float     SwLinearSweep(float current, float target, float speed) { if (current < target) return SwMin(current + speed, target); if (current > target) return SwMax(current - speed, target); return current; }
static inline SwVec2    SwMul(const SwVec2& lhs, const SwVec2& rhs) { return SwVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline bool      SwIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; }
SW_MSVC_RUNTIME_CHECKS_RESTORE


// Helper: SwVec1 (1D vector)
// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
SW_MSVC_RUNTIME_CHECKS_OFF
struct SwVec1
{
    float   x;
    constexpr  SwVec1() : x(0.0f) { }
    constexpr  SwVec1(float _x) : x(_x) { }
};

// Helper: SwVec2ih (2D vector, half-size integer, for long-term packed storage)
struct  SwVec2ih
{
    short   x, y;
    constexpr  SwVec2ih() : x(0), y(0) {}
    constexpr  SwVec2ih(short _x, short _y) : x(_x), y(_y) {}
    constexpr explicit  SwVec2ih(const  SwVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {}
};

// Helper: SwRect (2D axis aligned bounding-box)
// NB: we can't rely on SwVec2 math operators being available here!
struct SW_API  SwRect
{
    SwVec2   Min;    // Upper-left
    SwVec2   Max;    // Lower-right

    constexpr   SwRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {}
    constexpr   SwRect(const SwVec2& min, const SwVec2& max) : Min(min), Max(max) {}
    constexpr   SwRect(const SwVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
    constexpr   SwRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}

    SwVec2      GetCenter() const { return SwVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }
    SwVec2      GetSize() const { return SwVec2(Max.x - Min.x, Max.y - Min.y); }
    float       GetWidth() const { return Max.x - Min.x; }
    float       GetHeight() const { return Max.y - Min.y; }
    float       GetArea() const { return (Max.x - Min.x) * (Max.y - Min.y); }
    SwVec2      GetTL() const { return Min; }                   // Top-left
    SwVec2      GetTR() const { return SwVec2(Max.x, Min.y); }  // Top-right
    SwVec2      GetBL() const { return SwVec2(Min.x, Max.y); }  // Bottom-left
    SwVec2      GetBR() const { return Max; }                   // Bottom-right
    bool        Contains(const SwVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x&& p.y < Max.y; }
    bool        Contains(const SwRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }
    bool        Overlaps(const SwRect& r) const { return r.Min.y <  Max.y&& r.Max.y >  Min.y && r.Min.x <  Max.x&& r.Max.x >  Min.x; }
    void        Add(const SwVec2& p) { if (Min.x > p.x)     Min.x = p.x;     if (Min.y > p.y)     Min.y = p.y;     if (Max.x < p.x)     Max.x = p.x;     if (Max.y < p.y)     Max.y = p.y; }
    void        Add(const SwRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
    void        Expand(const float amount) { Min.x -= amount;   Min.y -= amount;   Max.x += amount;   Max.y += amount; }
    void        Expand(const SwVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
    void        Translate(const SwVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
    void        TranslateX(float dx) { Min.x += dx; Max.x += dx; }
    void        TranslateY(float dy) { Min.y += dy; Max.y += dy; }
    void        ClipWith(const SwRect& r) { Min = SwMax(Min, r.Min); Max = SwMin(Max, r.Max); }                   // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
    void        ClipWithFull(const SwRect& r) { Min = SwClamp(Min, r.Min, r.Max); Max = SwClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
    void        Floor() { Min.x = SW_FLOOR(Min.x); Min.y = SW_FLOOR(Min.y); Max.x = SW_FLOOR(Max.x); Max.y = SW_FLOOR(Max.y); }
    bool        IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
    SwVec4      ToVec4() const { return SwVec4(Min.x, Min.y, Max.x, Max.y); }
};
SW_MSVC_RUNTIME_CHECKS_RESTORE

// Helper: SwBitArray
inline bool     SwBitArrayTestBit(const SwU32* arr, int n) { SwU32 mask = (SwU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
inline void     SwBitArrayClearBit(SwU32* arr, int n) { SwU32 mask = (SwU32)1 << (n & 31); arr[n >> 5] &= ~mask; }
inline void     SwBitArraySetBit(SwU32* arr, int n) { SwU32 mask = (SwU32)1 << (n & 31); arr[n >> 5] |= mask; }
inline void     SwBitArraySetBitRange(SwU32* arr, int n, int n2) // Works on range [n..n2)
{
    n2--;
    while (n <= n2)
    {
        int a_mod = (n & 31);
        int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1;
        SwU32 mask = (SwU32)(((SwU64)1 << b_mod) - 1) & ~(SwU32)(((SwU64)1 << a_mod) - 1);
        arr[n >> 5] |= mask;
        n = (n + 32) & ~31;
    }
}

// Helper: SwBitArray class (wrapper over SwBitArray functions)
// Store 1-bit per value.
template<int BITCOUNT, int OFFSET = 0>
struct SwBitArray
{
    SwU32           Storage[(BITCOUNT + 31) >> 5];
    SwBitArray() { ClearAllBits(); }
    void            ClearAllBits() { memset(Storage, 0, sizeof(Storage)); }
    void            SetAllBits() { memset(Storage, 255, sizeof(Storage)); }
    bool            TestBit(int n) const { n += OFFSET; SW_ASSERT(n >= 0 && n < BITCOUNT); return SwBitArrayTestBit(Storage, n); }
    void            SetBit(int n) { n += OFFSET; SW_ASSERT(n >= 0 && n < BITCOUNT); SwBitArraySetBit(Storage, n); }
    void            ClearBit(int n) { n += OFFSET; SW_ASSERT(n >= 0 && n < BITCOUNT); SwBitArrayClearBit(Storage, n); }
    void            SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; SW_ASSERT(n >= 0 && n < BITCOUNT&& n2 > n && n2 <= BITCOUNT); SwBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
    bool            operator[](int n) const { n += OFFSET; SW_ASSERT(n >= 0 && n < BITCOUNT); return SwBitArrayTestBit(Storage, n); }
};

// Helper: SwBitVector
// Store 1-bit per value.
struct SW_API SwBitVector
{
    SwVector<SwU32> Storage;
    void            Create(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
    void            Clear() { Storage.clear(); }
    bool            TestBit(int n) const { SW_ASSERT(n < (Storage.Size << 5)); return SwBitArrayTestBit(Storage.Data, n); }
    void            SetBit(int n) { SW_ASSERT(n < (Storage.Size << 5)); SwBitArraySetBit(Storage.Data, n); }
    void            ClearBit(int n) { SW_ASSERT(n < (Storage.Size << 5)); SwBitArrayClearBit(Storage.Data, n); }
};

// Helper: SwSpan<>
// Pointing to a span of data we don't own.
template<typename T>
struct SwSpan
{
    T*                  Data;
    T*                  DataEnd;

    // Constructors, destructor
    inline              SwSpan() { Data = DataEnd = NULL; }
    inline              SwSpan(T* data, int size) { Data = data; DataEnd = data + size; }
    inline              SwSpan(T* data, T* data_end) { Data = data; DataEnd = data_end; }

    inline void         set(T* data, int size) { Data = data; DataEnd = data + size; }
    inline void         set(T* data, T* data_end) { Data = data; DataEnd = data_end; }
    inline int          size() const { return (int)(ptrdiff_t)(DataEnd - Data); }
    inline int          size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); }
    inline T& operator[](int i) { T* p = Data + i; SW_ASSERT(p >= Data && p < DataEnd); return *p; }
    inline const T& operator[](int i) const { const T* p = Data + i; SW_ASSERT(p >= Data && p < DataEnd); return *p; }

    inline T*           begin() { return Data; }
    inline const T*     begin() const { return Data; }
    inline T*           end() { return DataEnd; }
    inline const T*     end() const { return DataEnd; }

    // Utilities
    inline int          index_from_ptr(const T* it) const { SW_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; }
};

// Helper: SwSpanAllocator<>
// Facilitate storing multiple chunks into a single large block (the "arena")
// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges.
template<int CHUNKS>
struct SwSpanAllocator
{
    char*               BasePtr;
    int                 CurrOff;
    int                 CurrIdx;
    int                 Offsets[CHUNKS];
    int                 Sizes[CHUNKS];

    SwSpanAllocator() { memset(this, 0, sizeof(*this)); }
    inline void         Reserve(int n, size_t sz, int a = 4) { SW_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = SW_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; }
    inline int          GetArenaSizeInBytes() { return CurrOff; }
    inline void         SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; }
    inline void*        GetSpanPtrBegin(int n) { SW_ASSERT(n >= 0 && n < CHUNKS&& CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); }
    inline void*        GetSpanPtrEnd(int n) { SW_ASSERT(n >= 0 && n < CHUNKS&& CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); }
    template<typename T>
    inline void         GetSpan(int n, SwSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
};

// Helper: SwPool<>
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
typedef int SwPoolIdx;
template<typename T>
struct SwPool
{
    SwVector<T>       Buf;        // Contiguous data
    SwGuiStorage      Map;        // ID->Index
    SwPoolIdx         FreeIdx;    // Next free idx to use
    SwPoolIdx         AliveCount; // Number of active/alive items (for display purpose)

    SwPool() { FreeIdx = AliveCount = 0; }
    ~SwPool() { Clear(); }

    T*                  GetByKey(SwGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; }
    T*                  GetByIndex(SwPoolIdx n) { return &Buf[n]; }
    SwPoolIdx           GetIndex(const T* p) const { SW_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (SwPoolIdx)(p - Buf.Data); }
    T*                  GetOrAddByKey(SwGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); }
    bool                Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); }
    void                Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }
    T*                  Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } SW_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; }
    void                Remove(SwGuiID key, const T* p) { Remove(key, GetIndex(p)); }
    void                Remove(SwGuiID key, SwPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; }
    void                Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); }

    // To iterate a SwPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... }
    // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize()
    int                 GetAliveCount() const { return AliveCount; }      // Number of active/alive items in the pool (for display purpose)
    int                 GetBufSize() const { return Buf.Size; }
    int                 GetMapSize() const { return 0;/*return Map.Data.Size;*/ }   // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere
    T*                  TryGetMapData(SwPoolIdx n) { return NULL;/*int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx);*/ }
#ifndef SW_DISABLE_OBSOLETE_FUNCTIONS
    int                 GetSize() { return GetMapSize(); } // For SwPlot: should use GetMapSize() from (SW_VERSION_NUM >= 18304)
#endif
};

// Helper: SwChunkStream<>
// Build and iterate a contiguous stream of variable-sized structures.
// This is used by Settings to store persistent data while reducing allocation count.
// We store the chunk size first, and align the final size on 4 bytes boundaries.
// The tedious/zealous amount of casting is to avoid -Wcast-align warnings.
template<typename T>
struct SwChunkStream
{
    SwVector<char>    Buf;

    void                clear() { Buf.clear(); }
    bool                empty() const { return Buf.Size == 0; }
    int                 size() const { return Buf.Size; }
    T*                  alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = SW_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }
    T*                  begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }
    T*                  next_chunk(T* p) { size_t HDR_SZ = 4; SW_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; SW_ASSERT(p < end()); return p; }
    int                 chunk_size(const T* p) { return ((const int*)p)[-1]; }
    T*                  end() { return (T*)(void*)(Buf.Data + Buf.Size); }
    int                 offset_from_ptr(const T* p) { SW_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }
    T*                  ptr_from_offset(int off) { SW_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }
    void                swap(SwChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }

};


// Helpers: Geometry
SW_API SwGuiDir         SwGetDirQuadrantFromDelta(float dx, float dy);
SW_API bool             SwTriangleContainsPoint(const SwVec2& a, const SwVec2& b, const SwVec2& c, const SwVec2& p);


//-----------------------------------------------------------------------------
// [SECTION] SwDrawList support
//-----------------------------------------------------------------------------

// SwDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693
// Number of segments (N) is calculated using equation:
//   N = ceil ( pi / acos(1 - error / r) )     where r > 0, error <= r
// Our equation is significantly simpler that one in the post thanks for choosing segment that is
// perpendicular to X axis. Follow steps in the article from this starting condition and you will
// will get this result.
//
// Rendering circles with an odd number of segments, while mathematically correct will produce
// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.)
#define SW_ROUNDUP_TO_EVEN(_V)                                  ((((_V) + 1) / 2) * 2)
#define SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4
#define SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512
#define SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    SwClamp(SW_ROUNDUP_TO_EVEN((int)SwCeil(SW_PI / SwAcos(1 - SwMin((_MAXERROR), (_RAD)) / (_RAD)))), SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)

// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'.
#define SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR)    ((_MAXERROR) / (1 - SwCos(SW_PI / SwMax((float)(_N), SW_PI))))
#define SW_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD)     ((1 - SwCos(SW_PI / SwMax((float)(_N), SW_PI))) / (_RAD))

// SwDrawList: Lookup table size for adaptive arc drawing, cover full circle.
#ifndef SW_DRAWLIST_ARCFAST_TABLE_SIZE
#define SW_DRAWLIST_ARCFAST_TABLE_SIZE                          48 // Number of samples in lookup table.
#endif
#define SW_DRAWLIST_ARCFAST_SAMPLE_MAX                          SW_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle.

// Data shared between all SwDrawList instances
// You may want to create your own instance of this if you want to use SwDrawList completely without SwGui. In that case, watch out for future changes to this structure.
struct SW_API SwDrawListSharedData
{
    SwVec2              TexUvWhitePixel;            // UV of white pixel in the atlas
    SwFont*             Font;                       // Current/default font (optional, for simplified AddText overload)
    float               FontSize;                   // Current/default font size (optional, for simplified AddText overload)
    float               CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo()
    float               CircleSegmentMaxError;      // Number of circle segments to use per pixel of radius for AddCircle() etc
    SwVec4              ClipRectFullscreen;         // Value for PushClipRectFullscreen()
    SwDrawListFlags     InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)

    // [Internal] Lookup tables
    SwVec2              ArcFastVtx[SW_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
    float               ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
    SwU8                CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
    const SwVec4*       TexUvLines;                 // UV of anti-aliased lines in the atlas

    SwDrawListSharedData();
    void SetCircleTessellationMaxError(float max_error);
};

struct SwDrawDataBuilder
{
    SwVector<SwDrawList*>*  Layers[2];      // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.
    SwVector<SwDrawList*>   LayerData1;

    SwDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};

//-----------------------------------------------------------------------------
// [SECTION] Widgets support: flags, enums, data structures
//-----------------------------------------------------------------------------

// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin().
// This is going to be exposed in imgui.h when stabilized enough.
enum SwGuiItemFlags_
{
    // Controlled by user
    SwGuiItemFlags_None = 0,
    SwGuiItemFlags_NoTabStop = 1 << 0,  // false     // Disable keyboard tabbing. This is a "lighter" version of SwGuiItemFlags_NoNav.
    SwGuiItemFlags_ButtonRepeat = 1 << 1,  // false     // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.
    SwGuiItemFlags_Disabled = 1 << 2,  // false     // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211
    SwGuiItemFlags_NoNav = 1 << 3,  // false     // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls)
    SwGuiItemFlags_NoNavDefaultFocus = 1 << 4,  // false     // Disable item being a candidate for default focus (e.g. used by title bar items)
    SwGuiItemFlags_SelectableDontClosePopup = 1 << 5,  // false     // Disable MenuItem/Selectable() automatically closing their popup window
    SwGuiItemFlags_MixedValue = 1 << 6,  // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
    SwGuiItemFlags_ReadOnly = 1 << 7,  // false     // [ALPHA] Allow hovering interactions but underlying value is not changed.
    SwGuiItemFlags_NoWindowHoverableCheck = 1 << 8,  // false     // Disable hoverable check in ItemHoverable()
    SwGuiItemFlags_AllowOverlap = 1 << 9,  // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.

    // Controlled by widget code
    SwGuiItemFlags_Inputable = 1 << 10, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
    SwGuiItemFlags_HasSelectionUserData = 1 << 11, // false     // Set by SetNextItemSelectionUserData()
};

// Storage for LastItem data
enum SwGuiItemStatusFlags_
{
    SwGuiItemStatusFlags_None = 0,
    SwGuiItemStatusFlags_HoveredRect = 1 << 0,   // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)
    SwGuiItemStatusFlags_HasDisplayRect = 1 << 1,   // g.LastItemData.DisplayRect is valid
    SwGuiItemStatusFlags_Edited = 1 << 2,   // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
    SwGuiItemStatusFlags_ToggledSelection = 1 << 3,   // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues.
    SwGuiItemStatusFlags_ToggledOpen = 1 << 4,   // Set when TreeNode() reports toggling their open state.
    SwGuiItemStatusFlags_HasDeactivated = 1 << 5,   // Set if the widget/group is able to provide data for the SwGuiItemStatusFlags_Deactivated flag.
    SwGuiItemStatusFlags_Deactivated = 1 << 6,   // Only valid if SwGuiItemStatusFlags_HasDeactivated is set.
    SwGuiItemStatusFlags_HoveredWindow = 1 << 7,   // Override the HoveredWindow test to allow cross-window hover testing.
    SwGuiItemStatusFlags_FocusedByTabbing = 1 << 8,   // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)

#ifdef SW_ENABLE_TEST_ENGINE
    SwGuiItemStatusFlags_Openable = 1 << 20,  // Item is an openable (e.g. TreeNode)
    SwGuiItemStatusFlags_Opened = 1 << 21,  //
    SwGuiItemStatusFlags_Checkable = 1 << 22,  // Item is a checkable (e.g. CheckBox, MenuItem)
    SwGuiItemStatusFlags_Checked = 1 << 23,  //
#endif
};

// Extend SwGuiHoveredFlags_
enum SwGuiHoveredFlagsPrivate_
{
    SwGuiHoveredFlags_DelayMask_ = SwGuiHoveredFlags_DelayNone | SwGuiHoveredFlags_DelayShort | SwGuiHoveredFlags_DelayNormal | SwGuiHoveredFlags_NoSharedDelay,
    SwGuiHoveredFlags_AllowedMaskForIsWindowHovered = SwGuiHoveredFlags_ChildWindows | SwGuiHoveredFlags_RootWindow | SwGuiHoveredFlags_AnyWindow | SwGuiHoveredFlags_NoPopupHierarchy | SwGuiHoveredFlags_DockHierarchy | SwGuiHoveredFlags_AllowWhenBlockedByPopup | SwGuiHoveredFlags_AllowWhenBlockedByActiveItem | SwGuiHoveredFlags_ForTooltip | SwGuiHoveredFlags_Stationary,
    SwGuiHoveredFlags_AllowedMaskForIsItemHovered = SwGuiHoveredFlags_AllowWhenBlockedByPopup | SwGuiHoveredFlags_AllowWhenBlockedByActiveItem | SwGuiHoveredFlags_AllowWhenOverlapped | SwGuiHoveredFlags_AllowWhenDisabled | SwGuiHoveredFlags_NoNavOverride | SwGuiHoveredFlags_ForTooltip | SwGuiHoveredFlags_Stationary | SwGuiHoveredFlags_DelayMask_,
};

// Extend SwGuiInputTextFlags_
enum SwGuiInputTextFlagsPrivate_
{
    // [Internal]
    SwGuiInputTextFlags_Multiline = 1 << 26,  // For internal use by InputTextMultiline()
    SwGuiInputTextFlags_NoMarkEdited = 1 << 27,  // For internal use by functions using InputText() before reformatting data
    SwGuiInputTextFlags_MergedItem = 1 << 28,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
};

// Extend SwGuiButtonFlags_
enum SwGuiButtonFlagsPrivate_
{
    SwGuiButtonFlags_PressedOnClick = 1 << 4,   // return true on click (mouse down event)
    SwGuiButtonFlags_PressedOnClickRelease = 1 << 5,   // [Default] return true on click + release on same item <-- this is what the majority of Button are using
    SwGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item
    SwGuiButtonFlags_PressedOnRelease = 1 << 7,   // return true on release (default requires click+release)
    SwGuiButtonFlags_PressedOnDoubleClick = 1 << 8,   // return true on double-click (default requires click+release)
    SwGuiButtonFlags_PressedOnDragDropHold = 1 << 9,   // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
    SwGuiButtonFlags_Repeat = 1 << 10,  // hold to repeat
    SwGuiButtonFlags_FlattenChildren = 1 << 11,  // allow interactions even if a child window is overlapping
    SwGuiButtonFlags_AllowOverlap = 1 << 12,  // require previous frame HoveredId to either match id or be null before being usable.
    SwGuiButtonFlags_DontClosePopups = 1 << 13,  // disable automatically closing parent popup on press // [UNUSED]
    //SwGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or SwGuiItemFlags_Disabled
    SwGuiButtonFlags_AlignTextBaseLine = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
    SwGuiButtonFlags_NoKeyModifiers = 1 << 16,  // disable mouse interaction if a key modifier is held
    SwGuiButtonFlags_NoHoldingActiveId = 1 << 17,  // don't set ActiveId while holding the mouse (SwGuiButtonFlags_PressedOnClick only)
    SwGuiButtonFlags_NoNavFocus = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses SwGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
    SwGuiButtonFlags_NoHoveredOnFocus = 1 << 19,  // don't report as hovered when nav focus is on this item
    SwGuiButtonFlags_NoSetKeyOwner = 1 << 20,  // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be SwGuiKey_MouseLeft!)
    SwGuiButtonFlags_NoTestKeyOwner = 1 << 21,  // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be SwGuiKey_MouseLeft!)
    SwGuiButtonFlags_PressedOnMask_ = SwGuiButtonFlags_PressedOnClick | SwGuiButtonFlags_PressedOnClickRelease | SwGuiButtonFlags_PressedOnClickReleaseAnywhere | SwGuiButtonFlags_PressedOnRelease | SwGuiButtonFlags_PressedOnDoubleClick | SwGuiButtonFlags_PressedOnDragDropHold,
    SwGuiButtonFlags_PressedOnDefault_ = SwGuiButtonFlags_PressedOnClickRelease,
};

// Extend SwGuiComboFlags_
enum SwGuiComboFlagsPrivate_
{
    SwGuiComboFlags_CustomPreview = 1 << 20,  // enable BeginComboPreview()
};

// Extend SwGuiSliderFlags_
enum SwGuiSliderFlagsPrivate_
{
    SwGuiSliderFlags_Vertical = 1 << 20,  // Should this slider be orientated vertically?
    SwGuiSliderFlags_ReadOnly = 1 << 21,
};

// Extend SwGuiSelectableFlags_
enum SwGuiSelectableFlagsPrivate_
{
    // NB: need to be in sync with last value of SwGuiSelectableFlags_
    SwGuiSelectableFlags_NoHoldingActiveID = 1 << 20,
    SwGuiSelectableFlags_SelectOnNav = 1 << 21,  // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API.
    SwGuiSelectableFlags_SelectOnClick = 1 << 22,  // Override button behavior to react on Click (default is Click+Release)
    SwGuiSelectableFlags_SelectOnRelease = 1 << 23,  // Override button behavior to react on Release (default is Click+Release)
    SwGuiSelectableFlags_SpanAvailWidth = 1 << 24,  // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)
    SwGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25,  // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow.
    SwGuiSelectableFlags_SetNavIdOnHover = 1 << 26,  // Set Nav/Focus ID on mouse hover (used by MenuItem)
    SwGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27,  // Disable padding each side with ItemSpacing * 0.5f
};

// Extend SwGuiTreeNodeFlags_
enum SwGuiTreeNodeFlagsPrivate_
{
    SwGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20,
};

enum SwGuiSeparatorFlags_
{
    SwGuiSeparatorFlags_None = 0,
    SwGuiSeparatorFlags_Horizontal = 1 << 0,   // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar
    SwGuiSeparatorFlags_Vertical = 1 << 1,
    SwGuiSeparatorFlags_SpanAllColumns = 1 << 2,
};

// Flags for FocusWindow(). This is not called SwGuiFocusFlags to avoid confusion with public-facing SwGuiFocusedFlags.
// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf()
// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in.
enum SwGuiFocusRequestFlags_
{
    SwGuiFocusRequestFlags_None = 0,
    SwGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0,   // Find last focused child (if any) and focus it instead.
    SwGuiFocusRequestFlags_UnlessBelowModal = 1 << 1,   // Do not set focus if the window is below a modal.
};

enum SwGuiTextFlags_
{
    SwGuiTextFlags_None = 0,
    SwGuiTextFlags_NoWidthForLargeClippedText = 1 << 0,
};

enum SwGuiTooltipFlags_
{
    SwGuiTooltipFlags_None = 0,
    SwGuiTooltipFlags_OverridePreviousTooltip = 1 << 0,   // Override will clear/ignore previously submitted tooltip (defaults to append)
};

// FIXME: this is in development, not exposed/functional as a generic feature yet.
// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index SwVec2
enum SwGuiLayoutType_
{
    SwGuiLayoutType_Horizontal = 0,
    SwGuiLayoutType_Vertical = 1
};

enum SwGuiLogType
{
    SwGuiLogType_None = 0,
    SwGuiLogType_TTY,
    SwGuiLogType_File,
    SwGuiLogType_Buffer,
    SwGuiLogType_Clipboard,
};

// X/Y enums are fixed to 0/1 so they may be used to index SwVec2
enum SwGuiAxis
{
    SwGuiAxis_None = -1,
    SwGuiAxis_X = 0,
    SwGuiAxis_Y = 1
};

enum SwGuiPlotType
{
    SwGuiPlotType_Lines,
    SwGuiPlotType_Histogram,
};

enum SwGuiPopupPositionPolicy
{
    SwGuiPopupPositionPolicy_Default,
    SwGuiPopupPositionPolicy_ComboBox,
    SwGuiPopupPositionPolicy_Tooltip,
};

struct SwGuiDataVarInfo
{
    SwGuiDataType   Type;
    SwU32           Count;      // 1+
    SwU32           Offset;     // Offset in parent structure
    void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
};

struct SwGuiDataTypeTempStorage
{
    SwU8            Data[8];        // Can fit any data up to SwGuiDataType_COUNT
};

// Type information associated to one SwGuiDataType. Retrieve with DataTypeGetInfo().
struct SwGuiDataTypeInfo
{
    size_t          Size;           // Size in bytes
    const char*     Name;           // Short descriptive name for the type, for debugging
    const char*     PrintFmt;       // Default printf format for the type
    const char*     ScanFmt;        // Default scanf format for the type
};

// Extend SwGuiDataType_
enum SwGuiDataTypePrivate_
{
    SwGuiDataType_String = SwGuiDataType_COUNT + 1,
    SwGuiDataType_Pointer,
    SwGuiDataType_ID,
};

// Stacked color modifier, backup of modified data so we can restore it
struct SwGuiColorMod
{
    SwGuiCol        Col;
    SwVec4          BackupValue;
};

// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
struct SwGuiStyleMod
{
    SwGuiStyleVar       VarIdx;
    union { int BackupInt[2]; float BackupFloat[2]; };
    SwGuiStyleMod(SwGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; }
    SwGuiStyleMod(SwGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; }
    SwGuiStyleMod(SwGuiStyleVar idx, SwVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }
};

// Storage data for BeginComboPreview()/EndComboPreview()
struct SW_API SwGuiComboPreviewData
{
    SwRect              PreviewRect;
    SwVec2              BackupCursorPos;
    SwVec2              BackupCursorMaxPos;
    SwVec2              BackupCursorPosPrevLine;
    float               BackupPrevLineTextBaseOffset;
    SwGuiLayoutType     BackupLayout;

    SwGuiComboPreviewData() { memset(this, 0, sizeof(*this)); }
};


// Stacked storage data for BeginGroup()/EndGroup()
struct SW_API SwGuiGroupData
{
    SwGuiID             WindowID;
    SwVec2              BackupCursorPos;
    SwVec2              BackupCursorMaxPos;
    SwVec2              BackupCursorPosPrevLine;
    SwVec1              BackupIndent;
    SwVec1              BackupGroupOffset;
    SwVec2              BackupCurrLineSize;
    float               BackupCurrLineTextBaseOffset;
    SwGuiID             BackupActiveIdIsAlive;
    bool                BackupActiveIdPreviousFrameIsAlive;
    bool                BackupHoveredIdIsAlive;
    bool                EmitItem;
};

// Internal state of the currently focused/edited text input box
// For a given item ID, access with SW::GetInputTextState()
struct SW_API SwGuiInputTextState
{
    SwGuiContext*               Ctx;                    // parent UI context (needs to be set explicitly by parent).
    SwGuiID                     ID;                     // widget id owning the text state
    int                         CurLenW, CurLenA;       // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.
    SwVector<SwWchar>           TextW;                  // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
    SwVector<char>              TextA;                  // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
    SwVector<char>              InitialTextA;           // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
    bool                        TextAIsValid;           // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
    int                         BufCapacityA;           // end-user buffer capacity
    float                       ScrollX;                // horizontal scrolling/offset
    SwStb::STB_TexteditState    Stb;                   // state for stb_textedit.h
    float                       CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately
    bool                        CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)
    bool                        SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection
    bool                        Edited;                 // edited this frame
    SwGuiInputTextFlags         Flags;                  // copy of InputText() flags. may be used to check if e.g. SwGuiInputTextFlags_Password is set.

    SwGuiInputTextState() { memset(this, 0, sizeof(*this)); }
    void        ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
    void        ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); }
    int         GetUndoAvailCount() const { return Stb.undostate.undo_point; }
    int         GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }
    void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation

    // Cursor & Selection
    void        CursorAnimReset() { CursorAnim = -0.30f; }                                   // After a user-input the cursor stays on for a while without blinking
    void        CursorClamp() { Stb.cursor = SwMin(Stb.cursor, CurLenW); Stb.select_start = SwMin(Stb.select_start, CurLenW); Stb.select_end = SwMin(Stb.select_end, CurLenW); }
    bool        HasSelection() const { return Stb.select_start != Stb.select_end; }
    void        ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; }
    int         GetCursorPos() const { return Stb.cursor; }
    int         GetSelectionStart() const { return Stb.select_start; }
    int         GetSelectionEnd() const { return Stb.select_end; }
    void        SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }
};

// Storage for current popup stack
struct SwGuiPopupData
{
    SwGuiID             PopupId;        // Set on OpenPopup()
    SwGuiWindow*        Window;         // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
    SwGuiWindow*        BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close
    int                 ParentNavLayer; // Resolved on BeginPopup(). Actually a SwGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
    int                 OpenFrameCount; // Set on OpenPopup()
    SwGuiID             OpenParentId;   // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
    SwVec2              OpenPopupPos;   // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
    SwVec2              OpenMousePos;   // Set on OpenPopup(), copy of mouse position at the time of opening popup

    SwGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }
};

enum SwGuiNextWindowDataFlags_
{
    SwGuiNextWindowDataFlags_None                 = 0,
    SwGuiNextWindowDataFlags_HasPos               = 1 << 0,
    SwGuiNextWindowDataFlags_HasSize              = 1 << 1,
    SwGuiNextWindowDataFlags_HasContentSize       = 1 << 2,
    SwGuiNextWindowDataFlags_HasCollapsed         = 1 << 3,
    SwGuiNextWindowDataFlags_HasSizeConstraint    = 1 << 4,
    SwGuiNextWindowDataFlags_HasFocus             = 1 << 5,
    SwGuiNextWindowDataFlags_HasBgAlpha           = 1 << 6,
    SwGuiNextWindowDataFlags_HasScroll            = 1 << 7,
    SwGuiNextWindowDataFlags_HasChildFlags        = 1 << 8,
    SwGuiNextWindowDataFlags_HasViewport          = 1 << 9,
    SwGuiNextWindowDataFlags_HasDock              = 1 << 10,
    SwGuiNextWindowDataFlags_HasWindowClass       = 1 << 11,
};

// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.
struct SW_API SwGuiMenuColumns
{
    SwU32       TotalWidth;
    SwU32       NextTotalWidth;
    SwU16       Spacing;
    SwU16       OffsetIcon;         // Always zero for now
    SwU16       OffsetLabel;        // Offsets are locked in Update()
    SwU16       OffsetShortcut;
    SwU16       OffsetMark;
    SwU16       Widths[4];          // Width of:   Icon, Label, Shortcut, Mark  (accumulators for current frame)

    SwGuiMenuColumns() { memset(this, 0, sizeof(*this)); }
    void        Update(float spacing, bool window_reappearing);
    float       DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark);
    void        CalcNextTotalWidth(bool update_offsets);
};

// Internal temporary state for deactivating InputText() instances.
struct SW_API SwGuiInputTextDeactivatedState
{
    SwGuiID            ID;              // widget id owning the text state (which just got deactivated)
    SwVector<char>     TextA;           // text buffer

    SwGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); }
    void    ClearFreeMemory() { ID = 0; TextA.clear(); }
};

// Storage for SetNexWindow** functions
struct SwGuiNextWindowData
{
    SwGuiNextWindowDataFlags        Flags;
    SwGuiCond                       PosCond;
    SwGuiCond                       SizeCond;
    SwGuiCond                       CollapsedCond;
    SwGuiCond                       DockCond;
    SwVec2                          PosVal;
    SwVec2                          PosPivotVal;
    SwVec2                          SizeVal;
    SwVec2                          ContentSizeVal;
    SwVec2                          ScrollVal;
    SwGuiChildFlags                 ChildFlags;
    bool                            PosUndock;
    bool                            CollapsedVal;
    SwRect                          SizeConstraintRect;
    SwGuiSizeCallback               SizeCallback;
    void* SizeCallbackUserData;
    float                           BgAlphaVal;             // Override background alpha
    SwGuiID                         ViewportId;
    SwGuiID                         DockId;
    SwGuiWindowClass                WindowClass;
    SwVec2                          MenuBarOffsetMinVal;    // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)

    SwGuiNextWindowData() { memset(this, 0, sizeof(*this)); }
    inline void ClearFlags() { Flags = SwGuiNextWindowDataFlags_None; }
};

// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect()
// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.)
typedef SwS64 SwGuiSelectionUserData;

enum SwGuiNextItemDataFlags_
{
    SwGuiNextItemDataFlags_None = 0,
    SwGuiNextItemDataFlags_HasWidth = 1 << 0,
    SwGuiNextItemDataFlags_HasOpen = 1 << 1,
};

struct SwGuiNextItemData
{
    SwGuiNextItemDataFlags          Flags;
    SwGuiItemFlags                  ItemFlags;          // Currently only tested/used for SwGuiItemFlags_AllowOverlap.
    // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()
    float                           Width;              // Set by SetNextItemWidth()
    SwGuiSelectionUserData          SelectionUserData;  // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == SwGuiSelectionUserData_Invalid to mark invalid values)
    SwGuiCond                       OpenCond;
    bool                            OpenVal;            // Set by SetNextItemOpen()

    SwGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
    inline void ClearFlags() { Flags = SwGuiNextItemDataFlags_None; ItemFlags = SwGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
};

// Status storage for the last submitted item
struct SwGuiLastItemData
{
    SwGuiID                     ID;
    SwGuiItemFlags              InFlags;            // See SwGuiItemFlags_
    SwGuiItemStatusFlags        StatusFlags;        // See SwGuiItemStatusFlags_
    SwRect                      Rect;               // Full rectangle
    SwRect                      NavRect;            // Navigation scoring rectangle (not displayed)
    SwRect                      DisplayRect;        // Display rectangle (only if SwGuiItemStatusFlags_HasDisplayRect is set)

    SwGuiLastItemData() { memset(this, 0, sizeof(*this)); }
};


struct SW_API SwGuiStackSizes
{
    short   SizeOfIDStack;
    short   SizeOfColorStack;
    short   SizeOfStyleVarStack;
    short   SizeOfFontStack;
    short   SizeOfFocusScopeStack;
    short   SizeOfGroupStack;
    short   SizeOfItemFlagsStack;
    short   SizeOfBeginPopupStack;
    short   SizeOfDisabledStack;

    SwGuiStackSizes() { memset(this, 0, sizeof(*this)); }
    void SetToCurrentState();
    void CompareWithCurrentState();
};

// Data saved for each window pushed into the stack
struct SwGuiWindowStackData
{
    SwGuiWindow*            Window;
    SwGuiLastItemData       ParentLastItemDataBackup;
    SwGuiStackSizes         StackSizesOnBegin;      // Store size of various stacks for asserting
};

struct SwGuiShrinkWidthItem
{
    int         Index;
    float       Width;
    float       InitialWidth;
};

struct SwGuiPtrOrIndex
{
    void*       Ptr;            // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.
    int         Index;          // Usually index in a main pool.

    SwGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; }
    SwGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
};


//-----------------------------------------------------------------------------
// [SECTION] Inputs support
//-----------------------------------------------------------------------------

typedef SwBitArray<SwGuiKey_NamedKey_COUNT, -SwGuiKey_NamedKey_BEGIN>    SwBitArrayForNamedKeys;

// Extend SwGuiKey_
#define SwGuiKey_LegacyNativeKey_BEGIN  0
#define SwGuiKey_LegacyNativeKey_END    512
#define SwGuiKey_Keyboard_BEGIN         (SwGuiKey_NamedKey_BEGIN)
#define SwGuiKey_Keyboard_END           (SwGuiKey_GamepadStart)
#define SwGuiKey_Gamepad_BEGIN          (SwGuiKey_GamepadStart)
#define SwGuiKey_Gamepad_END            (SwGuiKey_GamepadRStickDown + 1)
#define SwGuiKey_Aliases_BEGIN          (SwGuiKey_MouseLeft)
#define SwGuiKey_Aliases_END            (SwGuiKey_COUNT)

// [Internal] Named shortcuts for Navigation
#define SwGuiKey_NavKeyboardTweakSlow   SwGuiKey_ModCtrl
#define SwGuiKey_NavKeyboardTweakFast   SwGuiKey_ModShift
#define SwGuiKey_NavGamepadTweakSlow    SwGuiKey_GamepadL1
#define SwGuiKey_NavGamepadTweakFast    SwGuiKey_GamepadR1
#define SwGuiKey_NavGamepadActivate     SwGuiKey_GamepadFaceDown
#define SwGuiKey_NavGamepadCancel       SwGuiKey_GamepadFaceRight
#define SwGuiKey_NavGamepadMenu         SwGuiKey_GamepadFaceLeft
#define SwGuiKey_NavGamepadInput        SwGuiKey_GamepadFaceUp

enum SwGuiInputEventType
{
    SwGuiInputEventType_None = 0,
    SwGuiInputEventType_MousePos,
    SwGuiInputEventType_MouseWheel,
    SwGuiInputEventType_MouseButton,
    SwGuiInputEventType_MouseViewport,
    SwGuiInputEventType_Key,
    SwGuiInputEventType_Text,
    SwGuiInputEventType_Focus,
    SwGuiInputEventType_COUNT
};

enum SwGuiInputSource
{
    SwGuiInputSource_None = 0,
    SwGuiInputSource_Mouse,
    SwGuiInputSource_Keyboard,
    SwGuiInputSource_Gamepad,
    SwGuiInputSource_Clipboard,     // Currently only used by InputText()
    SwGuiInputSource_Nav,           // Stored in g.ActiveIdSource only
    SwGuiInputSource_COUNT
};

// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension?
// Using SwVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor'
struct SwGuiInputEventMousePos { float PosX, PosY; SwGuiMouseSource MouseSource; };
struct SwGuiInputEventMouseWheel { float WheelX, WheelY; SwGuiMouseSource MouseSource; };
struct SwGuiInputEventMouseButton { int Button; bool Down; SwGuiMouseSource MouseSource; };
struct SwGuiInputEventMouseViewport { SwGuiID HoveredViewportID; };
struct SwGuiInputEventKey { SwGuiKey Key; bool Down; float AnalogValue; };
struct SwGuiInputEventText { unsigned int Char; };
struct SwGuiInputEventAppFocused { bool Focused; };

struct SwGuiInputEvent
{
    SwGuiInputEventType             Type;
    SwGuiInputSource                Source;
    SwU32                           EventId;        // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data).
    union
    {
        SwGuiInputEventMousePos     MousePos;       // if Type == SwGuiInputEventType_MousePos
        SwGuiInputEventMouseWheel   MouseWheel;     // if Type == SwGuiInputEventType_MouseWheel
        SwGuiInputEventMouseButton  MouseButton;    // if Type == SwGuiInputEventType_MouseButton
        SwGuiInputEventMouseViewport MouseViewport; // if Type == SwGuiInputEventType_MouseViewport
        SwGuiInputEventKey          Key;            // if Type == SwGuiInputEventType_Key
        SwGuiInputEventText         Text;           // if Type == SwGuiInputEventType_Text
        SwGuiInputEventAppFocused   AppFocused;     // if Type == SwGuiInputEventType_Focus
    };
    bool                            AddedByTestEngine;

    SwGuiInputEvent() { memset(this, 0, sizeof(*this)); }
};

// Input function taking an 'SwGuiID owner_id' argument defaults to (SwGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.
#define SwGuiKeyOwner_Any           ((SwGuiID)0)    // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used SwGuiInputFlags_LockThisFrame or SwGuiInputFlags_LockUntilRelease.
#define SwGuiKeyOwner_None          ((SwGuiID)-1)   // Require key to have no owner.

typedef SwS16 SwGuiKeyRoutingIndex;

// Routing table entry (sizeof() == 16 bytes)
struct SwGuiKeyRoutingData
{
    SwGuiKeyRoutingIndex            NextEntryIndex;
    SwU16                           Mods;               // Technically we'd only need 4-bits but for simplify we store SwGuiMod_ values which need 16-bits. SwGuiMod_Shortcut is already translated to Ctrl/Super.
    SwU8                            RoutingNextScore;   // Lower is better (0: perfect score)
    SwGuiID                         RoutingCurr;
    SwGuiID                         RoutingNext;

    SwGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = SwGuiKeyOwner_None; }
};

// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
// Stored in main context (1 instance)
struct SwGuiKeyRoutingTable
{
    SwGuiKeyRoutingIndex              Index[SwGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]
    SwVector<SwGuiKeyRoutingData>   Entries;
    SwVector<SwGuiKeyRoutingData>   EntriesNext;                    // Double-buffer to avoid reallocation (could use a shared buffer)

    SwGuiKeyRoutingTable() { Clear(); }
    void Clear() { for (int n = 0; n < SW_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }
};

// This extends SwGuiKeyData but only for named keys (legacy keys don't support the new features)
// Stored in main context (1 per named key). In the future it might be merged into SwGuiKeyData.
struct SwGuiKeyOwnerData
{
    SwGuiID         OwnerCurr;
    SwGuiID         OwnerNext;
    bool            LockThisFrame;      // Reading this key requires explicit owner id (until end of frame). Set by SwGuiInputFlags_LockThisFrame.
    bool            LockUntilRelease;   // Reading this key requires explicit owner id (until key is released). Set by SwGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.

    SwGuiKeyOwnerData() { OwnerCurr = OwnerNext = SwGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; }
};

// Flags for IsKeyPressed(). In upcoming feature this will be used more (and IsKeyPressed() renamed)
// Don't mistake with SwGuiInputTextFlags! (for SW::InputText() function)
enum SwGuiInputFlags_
{
    // Flags for IsKeyPressed(), IsMouseClicked(), Shortcut()
    SwGuiInputFlags_None = 0,
    SwGuiInputFlags_Repeat = 1 << 0,   // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
    SwGuiInputFlags_RepeatRateDefault = 1 << 1,   // Repeat rate: Regular (default)
    SwGuiInputFlags_RepeatRateNavMove = 1 << 2,   // Repeat rate: Fast
    SwGuiInputFlags_RepeatRateNavTweak = 1 << 3,   // Repeat rate: Faster
    SwGuiInputFlags_RepeatRateMask_ = SwGuiInputFlags_RepeatRateDefault | SwGuiInputFlags_RepeatRateNavMove | SwGuiInputFlags_RepeatRateNavTweak,

    // Flags for SetItemKeyOwner()
    SwGuiInputFlags_CondHovered = 1 << 4,   // Only set if item is hovered (default to both)
    SwGuiInputFlags_CondActive = 1 << 5,   // Only set if item is active (default to both)
    SwGuiInputFlags_CondDefault_ = SwGuiInputFlags_CondHovered | SwGuiInputFlags_CondActive,
    SwGuiInputFlags_CondMask_ = SwGuiInputFlags_CondHovered | SwGuiInputFlags_CondActive,

    // Flags for SetKeyOwner(), SetItemKeyOwner()
    SwGuiInputFlags_LockThisFrame = 1 << 6,   // Access to key data will require EXPLICIT owner ID (SwGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
    SwGuiInputFlags_LockUntilRelease = 1 << 7,   // Access to key data will require EXPLICIT owner ID (SwGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.

    // Routing policies for Shortcut() + low-level SetShortcutRouting()
    // - The general idea is that several callers register interest in a shortcut, and only one owner gets it.
    // - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(),
    //   allowing the system to decide where to route the input among other route-aware calls.
    // - Shortcut() uses SwGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll
    //   will register a route and only succeed when parent window is in the focus stack and if no-one
    //   with a higher priority is claiming the shortcut.
    // - Using SwGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods.
    // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow.
    // - Can select only 1 policy among all available.
    SwGuiInputFlags_RouteFocused = 1 << 8,   // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.
    SwGuiInputFlags_RouteGlobalLow = 1 << 9,   // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority.
    SwGuiInputFlags_RouteGlobal = 1 << 10,  // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText).
    SwGuiInputFlags_RouteGlobalHigh = 1 << 11,  // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items)
    SwGuiInputFlags_RouteMask_ = SwGuiInputFlags_RouteFocused | SwGuiInputFlags_RouteGlobal | SwGuiInputFlags_RouteGlobalLow | SwGuiInputFlags_RouteGlobalHigh, // _Always not part of this!
    SwGuiInputFlags_RouteAlways = 1 << 12,  // Do not register route, poll keys directly.
    SwGuiInputFlags_RouteUnlessBgFocused = 1 << 13,  // Global routes will not be applied if underlying background/void is focused (== no Dear SwGui windows are focused). Useful for overlay applications.
    SwGuiInputFlags_RouteExtraMask_ = SwGuiInputFlags_RouteAlways | SwGuiInputFlags_RouteUnlessBgFocused,

    // [Internal] Mask of which function support which flags
    SwGuiInputFlags_SupportedByIsKeyPressed = SwGuiInputFlags_Repeat | SwGuiInputFlags_RepeatRateMask_,
    SwGuiInputFlags_SupportedByShortcut = SwGuiInputFlags_Repeat | SwGuiInputFlags_RepeatRateMask_ | SwGuiInputFlags_RouteMask_ | SwGuiInputFlags_RouteExtraMask_,
    SwGuiInputFlags_SupportedBySetKeyOwner = SwGuiInputFlags_LockThisFrame | SwGuiInputFlags_LockUntilRelease,
    SwGuiInputFlags_SupportedBySetItemKeyOwner = SwGuiInputFlags_SupportedBySetKeyOwner | SwGuiInputFlags_CondMask_,
};


//-----------------------------------------------------------------------------
// [SECTION] Clipper support
//-----------------------------------------------------------------------------

struct SwGuiListClipperRange
{
    int         Min;
    int         Max;
    bool        PosToIndexConvert;      // Begin/End are absolute position (will be converted to indices later)
    SwS8        PosToIndexOffsetMin;    // Add to Min after converting to indices
    SwS8        PosToIndexOffsetMax;    // Add to Min after converting to indices

    static SwGuiListClipperRange    FromIndices(int min, int max) { SwGuiListClipperRange r = { min, max, false, 0, 0 }; return r; }
    static SwGuiListClipperRange    FromPositions(float y1, float y2, int off_min, int off_max) { SwGuiListClipperRange r = { (int)y1, (int)y2, true, (SwS8)off_min, (SwS8)off_max }; return r; }
};

// Temporary clipper data, buffers shared/reused between instances
struct SwGuiListClipperData
{
    SwGuiListClipper*               ListClipper;
    float                           LossynessOffset;
    int                             StepNo;
    int                             ItemsFrozen;
    SwVector<SwGuiListClipperRange> Ranges;

    SwGuiListClipperData() { memset(this, 0, sizeof(*this)); }
    void                            Reset(SwGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); }
};

//-----------------------------------------------------------------------------
// [SECTION] Navigation support
//-----------------------------------------------------------------------------

enum SwGuiActivateFlags_
{
    SwGuiActivateFlags_None = 0,
    SwGuiActivateFlags_PreferInput = 1 << 0,       // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available.
    SwGuiActivateFlags_PreferTweak = 1 << 1,       // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available.
    SwGuiActivateFlags_TryToPreserveState = 1 << 2,       // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
};

// Early work-in-progress API for ScrollToItem()
enum SwGuiScrollFlags_
{
    SwGuiScrollFlags_None = 0,
    SwGuiScrollFlags_KeepVisibleEdgeX = 1 << 0,       // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]
    SwGuiScrollFlags_KeepVisibleEdgeY = 1 << 1,       // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]
    SwGuiScrollFlags_KeepVisibleCenterX = 1 << 2,       // If item is not visible: scroll to make the item centered on X axis [rarely used]
    SwGuiScrollFlags_KeepVisibleCenterY = 1 << 3,       // If item is not visible: scroll to make the item centered on Y axis
    SwGuiScrollFlags_AlwaysCenterX = 1 << 4,       // Always center the result item on X axis [rarely used]
    SwGuiScrollFlags_AlwaysCenterY = 1 << 5,       // Always center the result item on Y axis [default for Y axis for appearing window)
    SwGuiScrollFlags_NoScrollParent = 1 << 6,       // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).
    SwGuiScrollFlags_MaskX_ = SwGuiScrollFlags_KeepVisibleEdgeX | SwGuiScrollFlags_KeepVisibleCenterX | SwGuiScrollFlags_AlwaysCenterX,
    SwGuiScrollFlags_MaskY_ = SwGuiScrollFlags_KeepVisibleEdgeY | SwGuiScrollFlags_KeepVisibleCenterY | SwGuiScrollFlags_AlwaysCenterY,
};

enum SwGuiNavHighlightFlags_
{
    SwGuiNavHighlightFlags_None = 0,
    SwGuiNavHighlightFlags_TypeDefault = 1 << 0,
    SwGuiNavHighlightFlags_TypeThin = 1 << 1,
    SwGuiNavHighlightFlags_AlwaysDraw = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.
    SwGuiNavHighlightFlags_NoRounding = 1 << 3,
};

enum SwGuiNavMoveFlags_
{
    SwGuiNavMoveFlags_None = 0,
    SwGuiNavMoveFlags_LoopX = 1 << 0,   // On failed request, restart from opposite side
    SwGuiNavMoveFlags_LoopY = 1 << 1,
    SwGuiNavMoveFlags_WrapX = 1 << 2,   // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
    SwGuiNavMoveFlags_WrapY = 1 << 3,   // This is not super useful but provided for completeness
    SwGuiNavMoveFlags_AllowCurrentNavId = 1 << 4,   // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
    SwGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5,   // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
    SwGuiNavMoveFlags_ScrollToEdgeY = 1 << 6,   // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
    SwGuiNavMoveFlags_Forwarded = 1 << 7,
    SwGuiNavMoveFlags_DebugNoResult = 1 << 8,   // Dummy scoring for debug purpose, don't apply result
    SwGuiNavMoveFlags_FocusApi = 1 << 9,
    SwGuiNavMoveFlags_Tabbing = 1 << 10,  // == Focus + Activate if item is Inputable + DontChangeNavHighlight
    SwGuiNavMoveFlags_Activate = 1 << 11,
    SwGuiNavMoveFlags_DontSetNavHighlight = 1 << 12,  // Do not alter the visible state of keyboard vs mouse nav highlight
};

enum SwGuiNavLayer
{
    SwGuiNavLayer_Main = 0,    // Main scrolling layer
    SwGuiNavLayer_Menu = 1,    // Menu layer (access with Alt)
    SwGuiNavLayer_COUNT
};

struct SwGuiNavItemData
{
    SwGuiWindow*            Window;         // Init,Move    // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
    SwGuiID                 ID;             // Init,Move    // Best candidate item ID
    SwGuiID                 FocusScopeId;   // Init,Move    // Best candidate focus scope ID
    SwRect                  RectRel;        // Init,Move    // Best candidate bounding box in window relative space
    SwGuiItemFlags          InFlags;        // ????,Move    // Best candidate item flags
    float                   DistBox;        //      Move    // Best candidate box distance to current NavId
    float                   DistCenter;     //      Move    // Best candidate center distance to current NavId
    float                   DistAxial;      //      Move    // Best candidate axial distance to current NavId

    SwGuiNavItemData() { Clear(); }
    void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; }
};


//-----------------------------------------------------------------------------
// [SECTION] Columns support
//-----------------------------------------------------------------------------

// Flags for internal's BeginColumns(). Prefix using BeginTable() nowadays!
enum SwGuiOldColumnFlags_
{
    SwGuiOldColumnFlags_None = 0,
    SwGuiOldColumnFlags_NoBorder = 1 << 0,   // Disable column dividers
    SwGuiOldColumnFlags_NoResize = 1 << 1,   // Disable resizing columns when clicking on the dividers
    SwGuiOldColumnFlags_NoPreserveWidths = 1 << 2,   // Disable column width preservation when adjusting columns
    SwGuiOldColumnFlags_NoForceWithinWindow = 1 << 3,   // Disable forcing columns to fit within window
    SwGuiOldColumnFlags_GrowParentContentsSize = 1 << 4,   // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.

    // Obsolete names (will be removed)
#ifndef SW_DISABLE_OBSOLETE_FUNCTIONS
    SwGuiColumnsFlags_None =                      SwGuiOldColumnFlags_None,
    SwGuiColumnsFlags_NoBorder =                  SwGuiOldColumnFlags_NoBorder,
    SwGuiColumnsFlags_NoResize =                  SwGuiOldColumnFlags_NoResize,
    SwGuiColumnsFlags_NoPreserveWidths =          SwGuiOldColumnFlags_NoPreserveWidths,
    SwGuiColumnsFlags_NoForceWithinWindow =       SwGuiOldColumnFlags_NoForceWithinWindow,
    SwGuiColumnsFlags_GrowParentContentsSize =    SwGuiOldColumnFlags_GrowParentContentsSize,
#endif
};

struct SwGuiOldColumnData
{
    float                   OffsetNorm;             // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
    float                   OffsetNormBeforeResize;
    SwGuiOldColumnFlags     Flags;                  // Not exposed
    SwRect                  ClipRect;

    SwGuiOldColumnData() { memset(this, 0, sizeof(*this)); }
};

struct SwGuiOldColumns
{
    SwGuiID                 ID;
    SwGuiOldColumnFlags     Flags;
    bool                    IsFirstFrame;
    bool                    IsBeingResized;
    int                     Current;
    int                     Count;
    float                   OffMinX, OffMaxX;       // Offsets from HostWorkRect.Min.x
    float                   LineMinY, LineMaxY;
    float                   HostCursorPosY;         // Backup of CursorPos at the time of BeginColumns()
    float                   HostCursorMaxPosX;      // Backup of CursorMaxPos at the time of BeginColumns()
    SwRect                  HostInitialClipRect;    // Backup of ClipRect at the time of BeginColumns()
    SwRect                  HostBackupClipRect;     // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()
    SwRect                  HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns()
    SwVector<SwGuiOldColumnData> Columns;
    SwDrawListSplitter      Splitter;

    SwGuiOldColumns() { memset(this, 0, sizeof(*this)); }
};


//-----------------------------------------------------------------------------
// [SECTION] Multi-select support
//-----------------------------------------------------------------------------

// We always assume that -1 is an invalid value (which works for indices and pointers)
#define SwGuiSelectionUserData_Invalid        ((SwGuiSelectionUserData)-1)

#ifdef SW_HAS_MULTI_SELECT
// <this is filled in 'range_select' branch>
#endif // #ifdef SW_HAS_MULTI_SELECT

//-----------------------------------------------------------------------------
// [SECTION] Docking support
//-----------------------------------------------------------------------------

#define DOCKING_HOST_DRAW_CHANNEL_BG 0  // Dock host: background fill
#define DOCKING_HOST_DRAW_CHANNEL_FG 1  // Dock host: decorations and contents

#ifdef SW_HAS_DOCK

// Extend SwGuiDockNodeFlags_
enum SwGuiDockNodeFlagsPrivate_
{
    // [Internal]
    SwGuiDockNodeFlags_DockSpace = 1 << 10,  // Local, Saved  // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window.
    SwGuiDockNodeFlags_CentralNode = 1 << 11,  // Local, Saved  // The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor.
    SwGuiDockNodeFlags_NoTabBar = 1 << 12,  // Local, Saved  // Tab bar is completely unavailable. No triangle in the corner to enable it back.
    SwGuiDockNodeFlags_HiddenTabBar = 1 << 13,  // Local, Saved  // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar)
    SwGuiDockNodeFlags_NoWindowMenuButton = 1 << 14,  // Local, Saved  // Disable window/docking menu (that one that appears instead of the collapse button)
    SwGuiDockNodeFlags_NoCloseButton = 1 << 15,  // Local, Saved  //
    SwGuiDockNodeFlags_NoDocking = 1 << 16,  // Local, Saved  // Disable any form of docking in this dockspace or individual node. (On a whole dockspace, this pretty much defeat the purpose of using a dockspace at all). Note: when turned on, existing docked nodes will be preserved.
    SwGuiDockNodeFlags_NoDockingSplitMe = 1 << 17,  // [EXPERIMENTAL] Prevent another window/node from splitting this node.
    SwGuiDockNodeFlags_NoDockingSplitOther = 1 << 18,  // [EXPERIMENTAL] Prevent this node from splitting another window/node.
    SwGuiDockNodeFlags_NoDockingOverMe = 1 << 19,  // [EXPERIMENTAL] Prevent another window/node to be docked over this node.
    SwGuiDockNodeFlags_NoDockingOverOther = 1 << 20,  // [EXPERIMENTAL] Prevent this node to be docked over another window or non-empty node.
    SwGuiDockNodeFlags_NoDockingOverEmpty = 1 << 21,  // [EXPERIMENTAL] Prevent this node to be docked over an empty node (e.g. DockSpace with no other windows)
    SwGuiDockNodeFlags_NoResizeX = 1 << 22,  // [EXPERIMENTAL]
    SwGuiDockNodeFlags_NoResizeY = 1 << 23,  // [EXPERIMENTAL]
    SwGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0,
    SwGuiDockNodeFlags_NoResizeFlagsMask_ = SwGuiDockNodeFlags_NoResize | SwGuiDockNodeFlags_NoResizeX | SwGuiDockNodeFlags_NoResizeY,
    SwGuiDockNodeFlags_LocalFlagsMask_ = SwGuiDockNodeFlags_NoSplit | SwGuiDockNodeFlags_NoResizeFlagsMask_ | SwGuiDockNodeFlags_AutoHideTabBar | SwGuiDockNodeFlags_DockSpace | SwGuiDockNodeFlags_CentralNode | SwGuiDockNodeFlags_NoTabBar | SwGuiDockNodeFlags_HiddenTabBar | SwGuiDockNodeFlags_NoWindowMenuButton | SwGuiDockNodeFlags_NoCloseButton | SwGuiDockNodeFlags_NoDocking,
    SwGuiDockNodeFlags_LocalFlagsTransferMask_ = SwGuiDockNodeFlags_LocalFlagsMask_ & ~SwGuiDockNodeFlags_DockSpace,  // When splitting those flags are moved to the inheriting child, never duplicated
    SwGuiDockNodeFlags_SavedFlagsMask_ = SwGuiDockNodeFlags_NoResizeFlagsMask_ | SwGuiDockNodeFlags_DockSpace | SwGuiDockNodeFlags_CentralNode | SwGuiDockNodeFlags_NoTabBar | SwGuiDockNodeFlags_HiddenTabBar | SwGuiDockNodeFlags_NoWindowMenuButton | SwGuiDockNodeFlags_NoCloseButton | SwGuiDockNodeFlags_NoDocking
};

// Store the source authority (dock node vs window) of a field
enum SwGuiDataAuthority_
{
    SwGuiDataAuthority_Auto,
    SwGuiDataAuthority_DockNode,
    SwGuiDataAuthority_Window,
};

enum SwGuiDockNodeState
{
    SwGuiDockNodeState_Unknown,
    SwGuiDockNodeState_HostWindowHiddenBecauseSingleWindow,
    SwGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing,
    SwGuiDockNodeState_HostWindowVisible,
};

// sizeof() 156~192
struct SW_API SwGuiDockNode
{
    SwGuiID                   ID;
    SwGuiDockNodeFlags        SharedFlags;                // (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node)
    SwGuiDockNodeFlags        LocalFlags;                 // (Write) Flags specific to this node
    SwGuiDockNodeFlags        LocalFlagsInWindows;        // (Write) Flags specific to this node, applied from windows
    SwGuiDockNodeFlags        MergedFlags;                // (Read)  Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows)
    SwGuiDockNodeState        State;
    SwGuiDockNode*            ParentNode;
    SwGuiDockNode*            ChildNodes[2];              // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array.
    SwVector<SwGuiWindow*>    Windows;                    // Note: unordered list! Iterate TabBar->Tabs for user-order.
    SwGuiTabBar*              TabBar;
    SwVec2                    Pos;                        // Current position
    SwVec2                    Size;                       // Current size
    SwVec2                    SizeRef;                    // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size.
    SwGuiAxis                 SplitAxis;                  // [Split node only] Split axis (X or Y)
    SwGuiWindowClass          WindowClass;                // [Root node only]
    SwU32                     LastBgColor;

    SwGuiWindow*              HostWindow;
    SwGuiWindow*              VisibleWindow;              // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window.
    SwGuiDockNode*            CentralNode;                // [Root node only] Pointer to central node.
    SwGuiDockNode*            OnlyNodeWithWindows;        // [Root node only] Set when there is a single visible node within the hierarchy.
    int                       CountNodeWithWindows;       // [Root node only]
    int                       LastFrameAlive;             // Last frame number the node was updated or kept alive explicitly with DockSpace() + SwGuiDockNodeFlags_KeepAliveOnly
    int                       LastFrameActive;            // Last frame number the node was updated.
    int                       LastFrameFocused;           // Last frame number the node was focused.
    SwGuiID                   LastFocusedNodeId;          // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused.
    SwGuiID                   SelectedTabId;              // [Leaf node only] Which of our tab/window is selected.
    SwGuiID                   WantCloseTabId;             // [Leaf node only] Set when closing a specific tab/window.
    SwGuiID                   RefViewportId;              // Reference viewport ID from visible window when HostWindow == NULL.
    SwGuiDataAuthority        AuthorityForPos : 3;
    SwGuiDataAuthority        AuthorityForSize : 3;
    SwGuiDataAuthority        AuthorityForViewport : 3;
    bool                      IsVisible : 1; // Set to false when the node is hidden (usually disabled as it has no active window)
    bool                      IsFocused : 1;
    bool                      IsBgDrawnThisFrame : 1;
    bool                      HasCloseButton : 1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one.
    bool                      HasWindowMenuButton : 1;
    bool                      HasCentralNodeChild : 1;
    bool                      WantCloseAll : 1; // Set when closing all tabs at once.
    bool                      WantLockSizeOnce : 1;
    bool                      WantMouseMove : 1; // After a node extraction we need to transition toward moving the newly created host window
    bool                      WantHiddenTabBarUpdate : 1;
    bool                      WantHiddenTabBarToggle : 1;

    SwGuiDockNode(SwGuiID id);
    ~SwGuiDockNode();
    bool                        IsRootNode() const { return ParentNode == NULL; }
    bool                        IsDockSpace() const { return (MergedFlags & SwGuiDockNodeFlags_DockSpace) != 0; }
    bool                        IsFloatingNode() const { return ParentNode == NULL && (MergedFlags & SwGuiDockNodeFlags_DockSpace) == 0; }
    bool                        IsCentralNode() const { return (MergedFlags & SwGuiDockNodeFlags_CentralNode) != 0; }
    bool                        IsHiddenTabBar() const { return (MergedFlags & SwGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle
    bool                        IsNoTabBar() const { return (MergedFlags & SwGuiDockNodeFlags_NoTabBar) != 0; }     // Never show a tab bar
    bool                        IsSplitNode() const { return ChildNodes[0] != NULL; }
    bool                        IsLeafNode() const { return ChildNodes[0] == NULL; }
    bool                        IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; }
    SwRect                      Rect() const { return SwRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }

    void                        SetLocalFlags(SwGuiDockNodeFlags flags) { LocalFlags = flags; UpdateMergedFlags(); }
    void                        UpdateMergedFlags() { MergedFlags = SharedFlags | LocalFlags | LocalFlagsInWindows; }
};

// List of colors that are stored at the time of Begin() into Docked Windows.
// We currently store the packed colors in a simple array window->DockStyle.Colors[].
// A better solution may involve appending into a log of colors in SwGuiContext + store offsets into those arrays in SwGuiWindow,
// but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame.
enum SwGuiWindowDockStyleCol
{
    SwGuiWindowDockStyleCol_Text,
    SwGuiWindowDockStyleCol_Tab,
    SwGuiWindowDockStyleCol_TabHovered,
    SwGuiWindowDockStyleCol_TabActive,
    SwGuiWindowDockStyleCol_TabUnfocused,
    SwGuiWindowDockStyleCol_TabUnfocusedActive,
    SwGuiWindowDockStyleCol_COUNT
};

struct SwGuiWindowDockStyle
{
    SwU32 Colors[SwGuiWindowDockStyleCol_COUNT];
};

struct SwGuiDockContext
{
    SwGuiStorage                        Nodes;          // Map ID -> SwGuiDockNode*: Active nodes
    SwVector<SwGuiDockRequest>          Requests;
    SwVector<SwGuiDockNodeSettings>     NodesSettings;
    bool                                WantFullRebuild;
    SwGuiDockContext() { memset(this, 0, sizeof(*this)); }
};

#endif // #ifdef SW_HAS_DOCK

//-----------------------------------------------------------------------------
// [SECTION] Viewport support
//-----------------------------------------------------------------------------

// SwGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!)
// Every instance of SwGuiViewport is in fact a SwGuiViewportP.
struct SwGuiViewportP : public SwGuiViewport
{
    int                     Idx;
    int                     LastFrameActive;        // Last frame number this viewport was activated by a window
    int                     LastFocusedStampCount;// Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order
    SwGuiID                 LastNameHash;
    SwVec2                  LastPos;
    float                   Alpha;                  // Window opacity (when dragging dockable windows/viewports we make them transparent)
    float                   LastAlpha;
    bool                    LastFocusedHadNavWindow;// Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow != NULL last time the viewport was focused.
    short                   PlatformMonitor;
    SwGuiWindow*            Window;                 // Set when the viewport is owned by a window (and SwGuiViewportFlags_CanHostOtherWindows is NOT set)
    int                     BgFgDrawListsLastFrame[2];  // Last frame number the background (0) and foreground (1) draw lists were used
    SwDrawList*             BgFgDrawLists[2];       // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.  
    SwDrawData              DrawDataP;
    SwDrawDataBuilder       DrawDataBuilder;
    SwVec2                  LastPlatformPos;
    SwVec2                  LastPlatformSize;
    SwVec2                  LastRendererSize;
    SwVec2                  WorkOffsetMin;          // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)
    SwVec2                  WorkOffsetMax;          // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).
    SwVec2                  BuildWorkOffsetMin;     // Work Area: Offset being built during current frame. Generally >= 0.0f.
    SwVec2                  BuildWorkOffsetMax;     // Work Area: Offset being built during current frame. Generally <= 0.0f.

    SwGuiViewportP() { Window = NULL; Idx = -1; LastFrameActive = BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = LastFocusedStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; LastFocusedHadNavWindow = false; PlatformMonitor = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = SwVec2(FLT_MAX, FLT_MAX); }
    ~SwGuiViewportP() { if (BgFgDrawLists[0]) SW_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) SW_DELETE(BgFgDrawLists[1]); }

    void                    ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; }

    // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
    SwVec2                  CalcWorkRectPos(const SwVec2& off_min) const { return SwVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
    SwVec2                  CalcWorkRectSize(const SwVec2& off_min, const SwVec2& off_max) const { return SwVec2(SwMax(0.0f, Size.x - off_min.x + off_max.x), SwMax(0.0f, Size.y - off_min.y + off_max.y)); }
    void                    UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields

    // Helpers to retrieve SwRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
    SwRect                  GetMainRect() const { return SwRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
    SwRect                  GetWorkRect() const { return SwRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
    SwRect                  GetBuildWorkRect() const { SwVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); SwVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return SwRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
};


//-----------------------------------------------------------------------------
// [SECTION] Settings support
//-----------------------------------------------------------------------------

// Windows data saved in imgui.ini file
// Because we never destroy or rename SwGuiWindowSettings, we can store the names in a separate buffer easily.
// (this is designed to be stored in a SwChunkStream buffer, with the variable-length Name following our structure)
struct SwGuiWindowSettings
{
    SwGuiID         ID;
    SwVec2ih        Pos;            // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions.
    SwVec2ih        Size;
    SwVec2ih        ViewportPos;
    SwGuiID         ViewportId;
    SwGuiID         DockId;         // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none.
    SwGuiID         ClassId;        // ID of window class if specified
    short           DockOrder;      // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.
    bool            Collapsed;
    bool            IsChild;
    bool            WantApply;      // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
    bool            WantDelete;     // Set to invalidate/delete the settings entry

    SwGuiWindowSettings() { memset(this, 0, sizeof(*this)); DockOrder = -1; }
    char* GetName() { return (char*)(this + 1); }
};

struct SwGuiSettingsHandler
{
    const char*     TypeName;       // Short description stored in .ini file. Disallowed characters: '[' ']'
    SwGuiID         TypeHash;       // == SwHashStr(TypeName)
    void            (*ClearAllFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler);                                // Clear all settings data
    void            (*ReadInitFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler);                                // Read: Called before reading (in registration order)
    void*           (*ReadOpenFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler, const char* name);              // Read: Called when entering into a new ini entry e.g. "[Window][Name]"
    void            (*ReadLineFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry
    void            (*ApplyAllFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler);                                // Read: Called after reading (in registration order)
    void            (*WriteAllFn)(SwGuiContext* ctx, SwGuiSettingsHandler* handler, SwGuiTextBuffer* out_buf);    // Write: Output every entries into 'out_buf'
    void*           UserData;

    SwGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }
};


//-----------------------------------------------------------------------------
// [SECTION] Metrics, Debug Tools
//-----------------------------------------------------------------------------

enum SwGuiDebugLogFlags_
{
    // Event types
    SwGuiDebugLogFlags_None = 0,
    SwGuiDebugLogFlags_EventActiveId = 1 << 0,
    SwGuiDebugLogFlags_EventFocus = 1 << 1,
    SwGuiDebugLogFlags_EventPopup = 1 << 2,
    SwGuiDebugLogFlags_EventNav = 1 << 3,
    SwGuiDebugLogFlags_EventClipper = 1 << 4,
    SwGuiDebugLogFlags_EventIO = 1 << 5,
    SwGuiDebugLogFlags_EventDocking = 1 << 6,
    SwGuiDebugLogFlags_EventViewport = 1 << 7,
    SwGuiDebugLogFlags_EventMask_ = SwGuiDebugLogFlags_EventActiveId | SwGuiDebugLogFlags_EventFocus | SwGuiDebugLogFlags_EventPopup | SwGuiDebugLogFlags_EventNav | SwGuiDebugLogFlags_EventClipper | SwGuiDebugLogFlags_EventIO,
    SwGuiDebugLogFlags_OutputToTTY = 1 << 10,  // Also send output to TTY
};

struct SwGuiMetricsConfig
{
    bool        ShowDebugLog;
    bool        ShowStackTool;
    bool        ShowWindowsRects;
    bool        ShowWindowsBeginOrder;
    bool        ShowTablesRects;
    bool        ShowDrawCmdMesh;
    bool        ShowDrawCmdBoundingBoxes;
    int         ShowWindowsRectsType;
    int         ShowTablesRectsType;

    SwGuiMetricsConfig()
    {
                ShowDebugLog = ShowStackTool = ShowWindowsRects = ShowWindowsBeginOrder = ShowTablesRects = false;
                ShowDrawCmdMesh = true;
                ShowDrawCmdBoundingBoxes = true;
                ShowWindowsRectsType = ShowTablesRectsType = -1;
    }
};

struct SwGuiStackLevelInfo
{
    SwGuiID                 ID;
    SwS8                    QueryFrameCount;            // >= 1: Query in progress
    bool                    QuerySuccess;               // Obtained result from DebugHookIdInfo()
    SwGuiDataType           DataType : 8;
    char                    Desc[57];                   // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed.

    SwGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
};

// State for Stack tool queries
struct SwGuiStackTool
{
    int                                 LastActiveFrame;
    int                                 StackLevel;                 // -1: query stack and resize Results, >= 0: individual stack level
    SwGuiID                             QueryId;                    // ID to query details for
    SwVector<SwGuiStackLevelInfo>       Results;
    bool                                CopyToClipboardOnCtrlC;
    float                               CopyToClipboardLastTime;

    SwGuiStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
};

//-----------------------------------------------------------------------------
// [SECTION] Generic context hooks
//-----------------------------------------------------------------------------

typedef void (*SwGuiContextHookCallback)(SwGuiContext* ctx, SwGuiContextHook* hook);
enum SwGuiContextHookType { SwGuiContextHookType_NewFramePre, SwGuiContextHookType_NewFramePost, SwGuiContextHookType_EndFramePre, SwGuiContextHookType_EndFramePost, SwGuiContextHookType_RenderPre, SwGuiContextHookType_RenderPost, SwGuiContextHookType_Shutdown, SwGuiContextHookType_PendingRemoval_ };

struct SwGuiContextHook
{
    SwGuiID                         HookId;     // A unique ID assigned by AddContextHook()
    SwGuiContextHookType            Type;
    SwGuiID                         Owner;
    SwGuiContextHookCallback        Callback;
    void*                           UserData;

    SwGuiContextHook() { memset(this, 0, sizeof(*this)); }
};


//-----------------------------------------------------------------------------
// [SECTION] SwGuiContext (main  SwGui context)
//-----------------------------------------------------------------------------

struct SwGuiContext
{
    bool                                    Initialized;
    bool                                    FontAtlasOwnedByContext;            // IO.Fonts-> is owned by the SwGuiContext and will be destructed along with it.
    SwGuiIO                                 IO;
    SwGuiPlatformIO                         PlatformIO;    
    SwGuiStyle                              Style;
    SwGuiConfigFlags                        ConfigFlagsCurrFrame;               // = g.IO.ConfigFlags at the time of NewFrame()
    SwGuiConfigFlags                        ConfigFlagsLastFrame;
    SwFont*                                 Font;                               // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
    float                                   FontSize;                           // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
    float                                   FontBaseSize;                       // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
    SwDrawListSharedData                    DrawListSharedData;
    double                                  Time;
    int                                     FrameCount;
    int                                     FrameCountEnded;
    int                                     FrameCountPlatformEnded;
    int                                     FrameCountRendered;
    bool                                    WithinFrameScope;                   // Set by NewFrame(), cleared by EndFrame()
    bool                                    WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
    bool                                    WithinEndChild;                     // Set within EndChild()
    bool                                    GcCompactAll;                       // Request full GC
    bool                                    TestEngineHookItems;                // Will call test engine hooks: SwGuiTestEngineHook_ItemAdd(), SwGuiTestEngineHook_ItemInfo(), SwGuiTestEngineHook_Log()
    void*                                   TestEngine;                         // Test engine user data


     // Inputs
    SwVector<SwGuiInputEvent>               InputEventsQueue;                 // Input events which will be trickled/written into IO structure.
    SwVector<SwGuiInputEvent>               InputEventsTrail;                 // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.
    SwGuiMouseSource                        InputEventsNextMouseSource;
    SwU32                                   InputEventsNextEventId;

    // Windows state
    SwVector<SwGuiWindow*>                  Windows;                            // Windows, sorted in display order, back to front
    SwVector<SwGuiWindow*>                  WindowsFocusOrder;                  // Root windows, sorted in focus order, back to front.
    SwVector<SwGuiWindow*>                  WindowsTempSortBuffer;              // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child
    SwVector<SwGuiWindowStackData>          CurrentWindowStack;
    SwGuiStorage                            WindowsById;                        // Map window's SwGuiID to SwGuiWindow*
    int                                     WindowsActiveCount;                 // Number of unique windows submitted by frame
    SwVec2                                  WindowsHoverPadding;                // Padding around resizable windows for which hovering on counts as hovering the window == SwMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)
    SwGuiWindow*                            CurrentWindow;                      // Window being drawn into
    SwGuiWindow*                            HoveredWindow;                      // Window the mouse is hovering. Will typically catch mouse inputs.
    SwGuiWindow*                            HoveredWindowUnderMovingWindow;     // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
    SwGuiWindow*                            MovingWindow;                       // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
    SwGuiWindow*                            WheelingWindow;                     // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
    SwVec2                                  WheelingWindowRefMousePos;
    float                                   WheelingWindowTimer;
    int                                     WheelingWindowStartFrame;           // This may be set one frame before WheelingWindow is != NULL
    int                                     WheelingWindowScrolledFrame;

    // Item/widgets state and tracking information
    SwGuiID                                 DebugHookIdInfo;                    // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
    SwGuiID                                 HoveredId;                          // Hovered widget, filled during the frame
    SwGuiID                                 HoveredIdPreviousFrame;
    bool                                    HoveredIdAllowOverlap;
    bool                                    HoveredIdUsingMouseWheel;           // Hovered widget will use mouse wheel. Blocks scrolling the underlying window.
    bool                                    HoveredIdPreviousFrameUsingMouseWheel;
    bool                                    HoveredIdDisabled;                  // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
    float                                   HoveredIdTimer;                     // Measure contiguous hovering time
    float                                   HoveredIdNotActiveTimer;            // Measure contiguous hovering time where the item has not been active
    SwGuiID                                 ActiveId;                           // Active widget
    SwGuiID                                 ActiveIdIsAlive;                    // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
    float                                   ActiveIdTimer;
    bool                                    ActiveIdIsJustActivated;            // Set at the time of activation for one frame
    bool                                    ActiveIdAllowOverlap;               // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
    bool                                    ActiveIdNoClearOnFocusLoss;         // Disable losing active id if the active id window gets unfocused.
    bool                                    ActiveIdHasBeenPressedBefore;       // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
    bool                                    ActiveIdHasBeenEditedBefore;        // Was the value associated to the widget Edited over the course of the Active state.
    bool                                    ActiveIdHasBeenEditedThisFrame;
    SwVec2                                  ActiveIdClickOffset;                // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
    SwGuiWindow*                            ActiveIdWindow;
    SwGuiInputSource                        ActiveIdSource;                     // Activating with mouse or nav (gamepad/keyboard)
    int                                     ActiveIdMouseButton;
    SwGuiID                                 ActiveIdPreviousFrame;
    bool                                    ActiveIdPreviousFrameIsAlive;
    bool                                    ActiveIdPreviousFrameHasBeenEditedBefore;
    SwGuiWindow*                            ActiveIdPreviousFrameWindow;
    SwGuiID                                 LastActiveId;                       // Store the last non-zero ActiveId, useful for animation.
    float                                   LastActiveIdTimer;                  // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.

    // [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system
    // - The idea is that instead of "eating" a given key, we can link to an owner.
    // - Input query can then read input by specifying SwGuiKeyOwner_Any (== 0), SwGuiKeyOwner_None (== -1) or a custom ID.
    // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().
    SwGuiKeyOwnerData                       KeysOwnerData[SwGuiKey_NamedKey_COUNT];
    SwGuiKeyRoutingTable                    KeysRoutingTable;
    SwU32                                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
    bool                                    ActiveIdUsingAllKeyboardKeys;       // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
#ifndef SW_DISABLE_OBSOLETE_KEYIO
    SwU32                                   ActiveIdUsingNavInputMask;          // If you used this. Since (SW_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << SwGuiNavInput_Cancel);' becomes 'SetKeyOwner(SwGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(SwGuiKey_NavGamepadCancel, g.ActiveId);'
#endif
     // Next window/item data
    SwGuiID                                 CurrentFocusScopeId;                // == g.FocusScopeStack.back()
    SwGuiItemFlags                          CurrentItemFlags;                   // == g.ItemFlagsStack.back()
    SwGuiNextItemData                       NextItemData;                       // Storage for SetNextItem** functions
    SwGuiLastItemData                       LastItemData;                       // Storage for last submitted item (setup by ItemAdd)
    SwGuiNextWindowData                     NextWindowData;                     // Storage for SetNextWindow** functions

    // Input Ownership
    SwBitArrayForNamedKeys                  ActiveIdUsingKeyInputMask;          // Active widget will want to read those key inputs. When we grow the SwGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.

    // Shared stacks
    SwVector<SwGuiColorMod>                 ColorStack;                         // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
    SwVector<SwGuiStyleMod>                 StyleVarStack;                      // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
    SwVector<SwFont*>                       FontStack;                          // Stack for PushFont()/PopFont() - inherited by Begin()
    SwVector<SwGuiID>                       FocusScopeStack;                    // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window
    SwVector<SwGuiItemFlags>                ItemFlagsStack;                     // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
    SwVector<SwGuiGroupData>                GroupStack;                         // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
    SwVector<SwGuiPopupData>                OpenPopupStack;                     // Which popups are open (persistent)
    SwVector<SwGuiPopupData>                BeginPopupStack;                    // Which level of BeginPopup() we are in (reset every frame)
    int                                     BeginMenuCount;

    // Viewports
    SwVector<SwGuiViewportP*>               Viewports;                        // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of SwDrawData.
    float                                   CurrentDpiScale;                    // == CurrentViewport->DpiScale
    SwGuiViewportP*                         CurrentViewport;                    // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport()
    SwGuiViewportP*                         MouseViewport;
    SwGuiViewportP*                         MouseLastHoveredViewport;           // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag.
    SwGuiID                                 PlatformLastFocusedViewportId;
    SwGuiPlatformMonitor                    FallbackMonitor;                    // Virtual monitor used as fallback if backend doesn't provide monitor information.
    int                                     ViewportCreatedCount;               // Unique sequential creation counter (mostly for testing/debugging)
    int                                     PlatformWindowsCreatedCount;        // Unique sequential creation counter (mostly for testing/debugging)
    int                                     ViewportFocusedStampCount;          // Every time the front-most window changes, we stamp its viewport with an incrementing counter

    bool                                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
    bool                                    NavInitRequest;                     // Init request for appearing window to select first item
    bool                                    NavInitRequestFromMove;
    SwGuiID                                 NavInitResultId;                    // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)
    SwRect                                  NavInitResultRectRel;               // Init request result rectangle (relative to parent window)
    bool                                    NavMoveSubmitted;                   // Move request submitted, will process result on next NewFrame()
    bool                                    NavMoveScoringItems;                // Move request submitted, still scoring incoming items
    bool                                    NavMoveForwardToNextFrame;
    SwGuiNavMoveFlags                       NavMoveFlags;
    SwGuiScrollFlags                        NavMoveScrollFlags;
    SwGuiModFlags                           NavMoveKeyMods;
    SwGuiDir                                NavMoveDir;                         // Direction of the move request (left/right/up/down)
    SwGuiDir                                NavMoveDirForDebug;
    SwGuiDir                                NavMoveClipDir;                     // FIXME-NAV: Describe the purpose of this better. Might want to rename?
    SwRect                                  NavScoringRect;                     // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
    SwRect                                  NavScoringNoClipRect;               // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted
    int                                     NavScoringDebugCount;               // Metrics for debugging
    int                                     NavTabbingDir;                      // Generally -1 or +1, 0 when tabbing without a nav id
    int                                     NavTabbingCounter;                  // >0 when counting items for tabbing
    SwGuiNavItemData                        NavMoveResultLocal;                 // Best move request candidate within NavWindow
    SwGuiNavItemData                        NavMoveResultLocalVisible;          // Best move request candidate within NavWindow that are mostly visible (when using SwGuiNavMoveFlags_AlsoScoreVisibleSet flag)
    SwGuiNavItemData                        NavMoveResultOther;                 // Best move request candidate within NavWindow's flattened hierarchy (when using SwGuiWindowFlags_NavFlattened flag)
    SwGuiNavItemData                        NavTabbingResultFirst;              // First tabbing request candidate within NavWindow and flattened hierarchy

    // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
    SwGuiWindow*                            NavWindowingTarget;                 // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
    SwGuiWindow*                            NavWindowingTargetAnim;             // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
    SwGuiWindow*                            NavWindowingListWindow;             // Internal window actually listing the CTRL+Tab contents
    float                                   NavWindowingTimer;
    float                                   NavWindowingHighlightAlpha;
    bool                                    NavWindowingToggleLayer;
    SwVec2                                  NavWindowingAccumDeltaPos;
    SwVec2                                  NavWindowingAccumDeltaSize;

    // Gamepad/keyboard Navigation
    SwGuiWindow*                            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'
    SwGuiID                                 NavId;                              // Focused item for navigation
    SwGuiID                                 NavFocusScopeId;                    // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set)
    SwGuiID                                 NavActivateId;                      // ~~ (g.ActiveId == 0) && (IsKeyPressed(SwGuiKey_Space) || IsKeyPressed(SwGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
    SwGuiID                                 NavActivateDownId;                  // ~~ IsKeyDown(SwGuiKey_Space) || IsKeyDown(SwGuiKey_NavGamepadActivate) ? NavId : 0
    SwGuiID                                 NavActivatePressedId;               // ~~ IsKeyPressed(SwGuiKey_Space) || IsKeyPressed(SwGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
    SwGuiID                                 NavActivateInputId;                 // ~~ IsKeyPressed(SwGuiKey_Enter) || IsKeyPressed(SwGuiKey_NavGamepadInput) ? NavId : 0; SwGuiActivateFlags_PreferInput will be set and NavActivateId will be 0.
    SwGuiActivateFlags                      NavActivateFlags;
    SwGuiID                                 NavJustMovedToId;                   // Just navigated to this id (result of a successfully MoveRequest).
    SwGuiID                                 NavJustMovedToFocusScopeId;         // Just navigated to this focus scope id (result of a successfully MoveRequest).
    SwGuiModFlags                           NavJustMovedToKeyMods;
    SwGuiID                                 NavNextActivateId;                  // Set by ActivateItem(), queued until next frame.
    SwGuiActivateFlags                      NavNextActivateFlags;
    SwGuiInputSource                        NavInputSource;                     // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
    SwGuiNavLayer                           NavLayer;                           // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
    SwGuiSelectionUserData                  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
    bool                                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
    bool                                    NavMousePosDirty;                   // When set we will update mouse position if (io.ConfigFlags & SwGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
    bool                                    NavDisableHighlight;                // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
    bool                                    NavDisableMouseHover;               // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.

    // Render
    float                                   DimBgRatio;                         // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
    SwGuiMouseCursor                        MouseCursor;

    // Drag and Drop
    bool                                    DragDropActive;
    bool                                    DragDropWithinSource;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source.
    bool                                    DragDropWithinTarget;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target.
    SwGuiDragDropFlags                      DragDropSourceFlags;
    int                                     DragDropSourceFrameCount;
    int                                     DragDropMouseButton;
    SwGuiPayload                            DragDropPayload;
    SwRect                                  DragDropTargetRect;                 // Store rectangle of current target candidate (we favor small targets when overlapping)
    SwGuiID                                 DragDropTargetId;
    SwGuiDragDropFlags                      DragDropAcceptFlags;
    float                                   DragDropAcceptIdCurrRectSurface;    // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)
    SwGuiID                                 DragDropAcceptIdCurr;               // Target item id (set at the time of accepting the payload)
    SwGuiID                                 DragDropAcceptIdPrev;               // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)
    int                                     DragDropAcceptFrameCount;           // Last time a target expressed a desire to accept the source
    SwGuiID                                 DragDropHoldJustPressedId;          // Set when holding a payload just made ButtonBehavior() return a press.
    SwVector<unsigned char>                 DragDropPayloadBufHeap;             // We don't expose the SwVector<> directly, SwGuiPayload only holds pointer+size
    unsigned char                           DragDropPayloadBufLocal[16];        // Local buffer for small payloads

    // Tab bars
    SwGuiTabBar*                            CurrentTabBar;
    SwPool<SwGuiTabBar>                     TabBars;
    SwVector<SwGuiPtrOrIndex>               CurrentTabBarStack;
    SwVector<SwGuiShrinkWidthItem>          ShrinkWidthBuffer;

    // Hover Delay system
    SwGuiID                                 HoverItemDelayId;
    SwGuiID                                 HoverItemDelayIdPreviousFrame;
    float                                   HoverItemDelayTimer;                // Currently used by IsItemHovered()
    float                                   HoverItemDelayClearTimer;           // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.
    SwGuiID                                 HoverItemUnlockedStationaryId;      // Mouse has once been stationary on this item. Only reset after departing the item.
    SwGuiID                                 HoverWindowUnlockedStationaryId;    // Mouse has once been stationary on this window. Only reset after departing the window.


    // Extensions
    // FIXME: We could provide an API to register one slot in an array held in SwGuiContext?
    SwGuiDockContext                        DockContext;


    // Settings
    bool                                    SettingsLoaded;
    float                                   SettingsDirtyTimer;                 // Save .ini Settings to memory when time reaches zero
    SwGuiTextBuffer                         SettingsIniData;                    // In memory .ini settings
    SwVector<SwGuiSettingsHandler>          SettingsHandlers;       // List of .ini settings handlers
    SwChunkStream<SwGuiWindowSettings>      SettingsWindows;        // SwGuiWindow .ini settings entries
    SwChunkStream<SwGuiTableSettings>       SettingsTables;         // SwGuiTable .ini settings entries
    SwVector<SwGuiContextHook>              Hooks;                  // Hooks for extensions (e.g. test engine)
    SwGuiID                                 HookIdNext;             // Next available HookId

    // Debug Tools
    SwGuiDebugLogFlags                      DebugLogFlags;
    SwGuiTextBuffer                         DebugLogBuf;
    bool                                    DebugItemPickerActive;              // Item picker is active (started with DebugStartItemPicker())
    SwU8                                    DebugItemPickerMouseButton;
    SwGuiID                                 DebugItemPickerBreakId;             // Will call SW_DEBUG_BREAK() when encountering this ID
    SwGuiMetricsConfig                      DebugMetricsConfig;
    SwGuiStackTool                          DebugStackTool;
    SwGuiDockNode*                          DebugHoveredDockNode;               // Hovered dock node.

    // Misc
    float                                   FramerateSecPerFrame[60];           // Calculate estimate of framerate for user over the last 60 frames..
    int                                     FramerateSecPerFrameIdx;
    int                                     FramerateSecPerFrameCount;
    float                                   FramerateSecPerFrameAccum;
    int                                     WantCaptureMouseNextFrame;          // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
    int                                     WantCaptureKeyboardNextFrame;       // "
    int                                     WantTextInputNextFrame;
    SwVector<char>                          TempBuffer;                         // Temporary text buffer

    // Clipper
    int                                     ClipperTempDataStacked;
    SwVector<SwGuiListClipperData>          ClipperTempData;

    // Tables
    SwGuiTable*                             CurrentTable;
    int                                     TablesTempDataStacked;      // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
    SwVector<SwGuiTableTempData>            TablesTempData;             // Temporary table data (buffers reused/shared across instances, support nesting)
    SwPool<SwGuiTable>                      Tables;                     // Persistent table data
    SwVector<float>                         TablesLastTimeActive;       // Last used timestamp of each tables (SOA, for efficient GC)
    SwVector<SwDrawChannel>                 DrawChannelsTempMergeBuffer;

    // Widget state
    SwVec2                                  MouseLastValidPos;
    SwGuiInputTextState                     InputTextState;
    SwGuiInputTextDeactivatedState          InputTextDeactivatedState;
    SwFont                                  InputTextPasswordFont;
    SwGuiID                                 TempInputId;                        // Temporary text input when CTRL+clicking on a slider, etc.
    SwGuiColorEditFlags                     ColorEditOptions;                   // Store user options for color edit widgets
    float                                   ColorEditLastHue;                   // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
    float                                   ColorEditLastSat;                   // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips
    SwU32                                   ColorEditLastColor;                 // RGB value with alpha set to 0.
    SwVec4                                  ColorPickerRef;                     // Initial/reference color at the time of opening the color picker.
    SwGuiComboPreviewData                   ComboPreviewData;
    float                                   SliderGrabClickOffset;
    float                                   SliderCurrentAccum;                 // Accumulated slider delta when using navigation controls.
    bool                                    SliderCurrentAccumDirty;            // Has the accumulated slider delta changed since last time we tried to apply it?
    bool                                    DragCurrentAccumDirty;
    float                                   DragCurrentAccum;                   // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
    float                                   DragSpeedDefaultRatio;              // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
    float                                   ScrollbarClickDeltaToGrabCenter;    // Distance between mouse and center of grab box, normalized in parent space. Use storage?
    float                                   DisabledAlphaBackup;                // Backup for style.Alpha for BeginDisabled()
    short                                   DisabledStackSize;
    short                                   TooltipOverrideCount;
    SwVector<char>                          ClipboardHandlerData;               // If no custom clipboard handler is defined
    SwVector<SwGuiID>                       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once
    SwRect                                  WindowResizeBorderExpectedRect;     // Expected border rect, switch to relative edit if moving
    bool                                    WindowResizeRelativeMode;


    // Platform support
    SwGuiPlatformImeData                    PlatformImeData;                    // Data updated by current frame
    SwGuiPlatformImeData                    PlatformImeDataPrev;                // Previous frame data (when changing we will call io.SetPlatformImeDataFn
    SwGuiID                                 PlatformImeViewport;
    char                                    PlatformLocaleDecimalPoint;         // '.' or *localeconv()->decimal_point

	// Capture/Logging
    bool                                    LogEnabled;                         // Currently capturing
    SwGuiLogType                            LogType;                            // Capture target
    SwFileHandle                            LogFile;                            // If != NULL log to stdout/ file
    SwGuiTextBuffer                         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GMUI_Ctx static constructor doesn't call heap allocators.
    const char*                             LogNextPrefix;
    const char*                             LogNextSuffix;
    float                                   LogLinePosY;
    bool                                    LogLineFirstItem;
    int                                     LogDepthRef;
    int                                     LogDepthToExpand;
    int                                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.

    SwGuiContext(SwFontAtlas* shared_font_atlas)
    {
        IO.Ctx = this;
        InputTextState.Ctx = this;

        Initialized = false;
        FontAtlasOwnedByContext = shared_font_atlas ? false : true;
        Font = NULL;
        FontSize = FontBaseSize = 0.0f;
        IO.Fonts = shared_font_atlas ? shared_font_atlas : SW_NEW(SwFontAtlas)();
        Time = 0.0f;
        FrameCount = 0;
        FrameCountEnded = FrameCountRendered = -1;
        WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
        GcCompactAll = false;
        TestEngineHookItems = false;
        TestEngine = NULL;

        InputEventsNextMouseSource = SwGuiMouseSource_Mouse;
        InputEventsNextEventId = 1;

        WindowsActiveCount = 0;
        CurrentWindow = NULL;
        HoveredWindow = NULL;
        HoveredWindowUnderMovingWindow = NULL;
        MovingWindow = NULL;
        WheelingWindow = NULL;
        WheelingWindowTimer = 0.0f;

        WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;

        DebugHookIdInfo = 0;
        HoveredId = HoveredIdPreviousFrame = 0;
        HoveredIdAllowOverlap = false;
        HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false;
        HoveredIdDisabled = false;
        HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
        ActiveId = 0;
        ActiveIdIsAlive = 0;
        ActiveIdTimer = 0.0f;
        ActiveIdIsJustActivated = false;
        ActiveIdAllowOverlap = false;
        ActiveIdNoClearOnFocusLoss = false;
        ActiveIdHasBeenPressedBefore = false;
        ActiveIdHasBeenEditedBefore = false;
        ActiveIdHasBeenEditedThisFrame = false;
        ActiveIdClickOffset = SwVec2(-1, -1);
        ActiveIdWindow = NULL;
        ActiveIdSource = SwGuiInputSource_None;
        ActiveIdMouseButton = -1;
        ActiveIdPreviousFrame = 0;
        ActiveIdPreviousFrameIsAlive = false;
        ActiveIdPreviousFrameHasBeenEditedBefore = false;
        ActiveIdPreviousFrameWindow = NULL;
        LastActiveId = 0;
        LastActiveIdTimer = 0.0f;

        ActiveIdUsingNavDirMask = 0x00;
        ActiveIdUsingKeyInputMask.ClearAllBits();
#ifndef SW_DISABLE_OBSOLETE_KEYIO
        ActiveIdUsingNavInputMask = 0x00;
#endif

        CurrentFocusScopeId = 0;
        CurrentItemFlags = SwGuiItemFlags_None;
        BeginMenuCount = 0;

        NavWindow = NULL;
        NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0;
        NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
        NavActivateFlags = NavNextActivateFlags = SwGuiActivateFlags_None;
        NavJustMovedToKeyMods = SwGuiModFlags_None;
        NavInputSource = SwGuiInputSource_None;
        NavLayer = SwGuiNavLayer_Main;
        NavIdIsAlive = false;
        NavMousePosDirty = false;
        NavDisableHighlight = true;
        NavDisableMouseHover = false;
        NavAnyRequest = false;
        NavInitRequest = false;
        NavInitRequestFromMove = false;
        NavInitResultId = 0;
        NavMoveSubmitted = false;
        NavMoveScoringItems = false;
        NavMoveForwardToNextFrame = false;
        NavMoveFlags = SwGuiNavMoveFlags_None;
        NavMoveScrollFlags = SwGuiScrollFlags_None;
        NavMoveKeyMods = SwGuiModFlags_None;
        NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = SwGuiDir_None;
        NavScoringDebugCount = 0;
        NavTabbingDir = 0;
        NavTabbingCounter = 0;

        NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
        NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
        NavWindowingToggleLayer = false;

        DimBgRatio = 0.0f;
        MouseCursor = SwGuiMouseCursor_Arrow;

        DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
        DragDropSourceFlags = SwGuiDragDropFlags_None;
        DragDropSourceFrameCount = -1;
        DragDropMouseButton = -1;
        DragDropTargetId = 0;
        DragDropAcceptFlags = SwGuiDragDropFlags_None;
        DragDropAcceptIdCurrRectSurface = 0.0f;
        DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
        DragDropAcceptFrameCount = -1;
        DragDropHoldJustPressedId = 0;
        memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));

        ClipperTempDataStacked = 0;

        CurrentTable = NULL;
        TablesTempDataStacked = 0;
        CurrentTabBar = NULL;

        TempInputId = 0;
        ColorEditOptions = SwGuiColorEditFlags_DefaultOptions_;
        ColorEditLastHue = ColorEditLastSat = 0.0f;
        ColorEditLastColor = 0;
        SliderGrabClickOffset = 0.0f;
        SliderCurrentAccum = 0.0f;
        SliderCurrentAccumDirty = false;
        DragCurrentAccumDirty = false;
        DragCurrentAccum = 0.0f;
        DragSpeedDefaultRatio = 1.0f / 100.0f;
        ScrollbarClickDeltaToGrabCenter = 0.0f;
        DisabledAlphaBackup = 0.0f;
        DisabledStackSize = 0;
        TooltipOverrideCount = 0;
        WindowResizeRelativeMode = false;

        PlatformImeData.InputPos = SwVec2(0.0f, 0.0f);
        PlatformImeDataPrev.InputPos = SwVec2(-1.0f, -1.0f); // Different to ensure initial submission
        PlatformImeViewport = 0;
        PlatformLocaleDecimalPoint = '.';

        SettingsLoaded = false;
        SettingsDirtyTimer = 0.0f;
        HookIdNext = 0;

        LogEnabled = false;
        LogType = SwGuiLogType_None;
        LogNextPrefix = LogNextSuffix = NULL;
        LogFile = NULL;
        LogLinePosY = FLT_MAX;
        LogLineFirstItem = false;
        LogDepthRef = 0;
        LogDepthToExpand = LogDepthToExpandDefault = 2;

        DebugLogFlags = SwGuiDebugLogFlags_OutputToTTY;
        DebugItemPickerActive = false;
        DebugItemPickerMouseButton = SwGuiMouseButton_Left;
        DebugItemPickerBreakId = 0;

        memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
        FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
        FramerateSecPerFrameAccum = 0.0f;
        WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;

        MouseLastHoveredViewport = NULL;
        ViewportCreatedCount = PlatformWindowsCreatedCount = 0;

        HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
        HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
    }
};


//-----------------------------------------------------------------------------
// [SECTION] SwGuiWindowTempData, SwGuiWindow
//-----------------------------------------------------------------------------

// Transient per-window data, reset at the beginning of the frame. This used to be called SwGuiDrawContext, hence the DC variable name in SwGuiWindow.
// (That's theory, in practice the delimitation between SwGuiWindow and SwGuiWindowTempData is quite tenuous and could be reconsidered..)
// (This doesn't need a constructor because we zero-clear it as part of SwGuiWindow and all frame-temporary data are setup on Begin)
struct SW_API SwGuiWindowTempData
{
    // Layout
    SwVec2                  CursorPos;              // Current emitting position, in absolute coordinates.
    SwVec2                  CursorPosPrevLine;
    SwVec2                  CursorStartPos;         // Initial position after Begin(), generally ~ window position + WindowPadding.
    SwVec2                  CursorMaxPos;           // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame.
    SwVec2                  IdealMaxPos;            // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame.
    SwVec2                  CurrLineSize;
    SwVec2                  PrevLineSize;
    float                   CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added).
    float                   PrevLineTextBaseOffset;
    bool                    IsSameLine;
    bool                    IsSetPos;
    SwVec1                  Indent;                 // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
    SwVec1                  ColumnsOffset;          // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.
    SwVec1                  GroupOffset;
    SwVec2                  CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area.

    // Last item status
    SwGuiID                 LastItemId;             // ID for last item
    SwGuiItemStatusFlags    LastItemStatusFlags;    // Status flags for last item (see SwGuiItemStatusFlags_)
    SwRect                  LastItemRect;           // Interaction rect for last item
    SwRect                  LastItemDisplayRect;    // End-user display rect for last item (only valid if LastItemStatusFlags & SwGuiItemStatusFlags_HasDisplayRect)

    // Keyboard/Gamepad navigation
    SwGuiNavLayer           NavLayerCurrent;        // Current layer, 0..31 (we currently only use 0..1)
    short                   NavLayersActiveMask;    // Which layers have been written to (result from previous frame)
    short                   NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)
    SwGuiID                 NavFocusScopeIdCurrent; // Current focus scope ID while appending
    bool                    NavHideHighlightOneFrame;
    bool                    NavWindowHasScrollY;    // Set per window when scrolling can be used (== ScrollMax.y > 0.0f)

    // Miscellaneous
    bool                    MenuBarAppending;       // FIXME: Remove this
    SwVec2                  MenuBarOffset;          // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
    SwGuiMenuColumns        MenuColumns;            // Simplified columns storage for menu items measurement
    int                     TreeDepth;              // Current tree depth.
    SwU32                   TreeJumpToParentOnPopMask; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a SwU64 if necessary.
    SwVector<SwGuiWindow*>ChildWindows;
    SwGuiStorage*           StateStorage;           // Current persistent per-window storage (store e.g. tree node open/close state)
    SwGuiOldColumns*        CurrentColumns;         // Current columns set
    int                     CurrentTableIdx;        // Current table index (into g.Tables)
    SwGuiLayoutType         LayoutType;
    SwGuiLayoutType         ParentLayoutType;       // Layout type of parent window at the time of Begin()

    // Local parameters stacks
    // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
    SwGuiItemFlags          ItemFlags;              // == ItemFlagsStack.back() [empty == SwGuiItemFlags_Default]
    float                   ItemWidth;              // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).
    float                   TextWrapPos;            // Current text wrap pos.
    SwVector<float>         ItemWidthStack;         // Store item widths to restore (attention: .back() is not == ItemWidth)
    SwVector<float>         TextWrapPosStack;       // Store text wrap pos to restore (attention: .back() is not == TextWrapPos)
    SwGuiStackSizes         StackSizesOnBegin;      // Store size of various stacks for asserting
    SwVector<SwGuiGroupData>GroupStack;
    short                   StackSizesBackup[6];    // Store size of various stacks for asserting

    SwGuiWindowTempData()
    {
       /* CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = SwVec2(0.0f, 0.0f);
        CurrLineSize = PrevLineSize = SwVec2(0.0f, 0.0f);
        CurrLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f;
        Indent = SwVec1(0.0f);
        ColumnsOffset = SwVec1(0.0f);
        GroupOffset = SwVec1(0.0f);*/

        LastItemId = 0;
        LastItemStatusFlags = SwGuiItemStatusFlags_None;
        LastItemRect = LastItemDisplayRect = SwRect();

       /* NavLayerActiveMask = NavLayerActiveMaskNext = 0x00;
        NavLayerCurrent = SwGuiNavLayer_Main;
        NavLayerCurrentMask = (1 << SwGuiNavLayer_Main);
        NavFocusScopeIdCurrent = 0;
        NavHideHighlightOneFrame = false;
        NavHasScroll = false;

        MenuBarAppending = false;
        MenuBarOffset = SwVec2(0.0f, 0.0f);
        TreeDepth = 0;
        TreeJumpToParentOnPopMask = 0x00;
        StateStorage = NULL;
        CurrentColumns = NULL;
        LayoutType = ParentLayoutType = SwGuiLayoutType_Vertical;
        FocusCounterRegular = FocusCounterTabStop = -1;

        ItemFlags = SwGuiItemFlags_Default_;
        ItemWidth = 0.0f;
        TextWrapPos = -1.0f;
        memset(StackSizesBackup, 0, sizeof(StackSizesBackup));*/
    }
};

// Storage for one window
struct SW_API SwGuiWindow
{
    SwGuiContext*           Ctx;                                // Parent UI context (needs to be set explicitly by parent).
    char*                   Name;                               // Window name, owned by the window.
    SwGuiID                 ID;                                 // == SwHashStr(Name)
    SwGuiWindowFlags        Flags, FlagsPreviousFrame;          // See enum SwGuiWindowFlags_
    SwGuiChildFlags         ChildFlags;                         // Set when window is a child window. See enum SwGuiChildFlags_
    SwGuiWindowClass        WindowClass;                        // Advanced users only. Set with SetNextWindowClass()
    SwGuiViewportP*         Viewport;                           // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.
    SwGuiID                 ViewportId;                         // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive)
    int                     ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor
    SwVec2                  ViewportPos;                        // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive)
    SwVec2                  Pos;                                // Position (always rounded-up to nearest pixel)
    SwVec2                  Size;                               // Current size (==SizeFull or collapsed title bar size)
    SwVec2                  SizeFull;                           // Size when non collapsed
    SwVec2                  ContentSize;                        // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.
    SwVec2                  ContentSizeIdeal;
    SwVec2                  ContentSizeExplicit;                // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().
    SwVec2                  WindowPadding;                      // Window padding at the time of Begin().
    float                   WindowRounding;                     // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
    float                   WindowBorderSize;                   // Window border size at the time of Begin().
    int                     NameBufLen;                         // Size of buffer storing Name. May be larger than strlen(Name)!
    SwGuiID                 MoveId;                             // == window->GetID("#MOVE")
    SwGuiID                 TabId;                              // == window->GetID("#TAB")
    SwGuiID                 ChildId;                            // ID of corresponding item in parent window (for navigation to return from child window to parent window)
    SwVec2                  Scroll;
    SwVec2                  ScrollMax;
    SwVec2                  ScrollTarget;                       // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
    SwVec2                  ScrollTargetCenterRatio;            // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
    SwVec2                  ScrollTargetEdgeSnapDist;           // 0.0f = no snapping, >0.0f snapping threshold
    SwVec2                  ScrollbarSizes;                     // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
    bool                    ScrollbarX, ScrollbarY;             // Are scrollbars visible?
    bool                    Active;                             // Set to true on Begin(), unless Collapsed
    bool                    ViewportOwned;
    bool                    WasActive;
    bool                    WriteAccessed;                      // Set to true when any widget access the current window
    bool                    Collapsed;                          // Set when collapsing window to become only title-bar
    bool                    WantCollapseToggle;
    bool                    SkipItems;                          // Set when items can safely be all clipped (e.g. window not visible or collapsed)
    bool                    Appearing;                          // Set during the frame where the window is appearing (or re-appearing)
    bool                    Hidden;                             // Do not display (== HiddenFrames*** > 0)
    bool                    IsFallbackWindow;                   // Set on the "Debug##Default" window.
    bool                    IsExplicitChild;                    // Set when passed _ChildWindow, left to false by BeginDocked()
    bool                    HasCloseButton;                     // Set when the window has a close button (p_open != NULL)
    signed char             ResizeBorderHeld;                   // Current border being held for resize (-1: none, otherwise 0-3)
    short                   BeginCount;                         // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
    short                   BeginCountPreviousFrame;            // Number of Begin() during the previous frame
    short                   BeginOrderWithinParent;             // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
    short                   BeginOrderWithinContext;            // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
    short                   FocusOrder;                         // Order within WindowsFocusOrder[], altered when windows are focused.
    SwGuiID                 PopupId;                            // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
    SwS8                    AutoFitFramesX, AutoFitFramesY;
    SwS8                    AutoFitChildAxises;
    bool                    AutoFitOnlyGrows;
    SwGuiDir                AutoPosLastDirection;
    SwS8                    HiddenFramesCanSkipItems;           // Hide the window for N frames
    SwS8                    HiddenFramesCannotSkipItems;        // Hide the window for N frames while allowing items to be submitted so we can measure their size
    SwS8                    HiddenFramesForRenderOnly;          // Hide the window until frame N at Render() time only
    SwS8                    DisableInputsFrames;                // Disable window interactions for N frames
    SwGuiCond               SetWindowPosAllowFlags : 8;         // store acceptable condition flags for SetNextWindowPos() use.
    SwGuiCond               SetWindowSizeAllowFlags : 8;        // store acceptable condition flags for SetNextWindowSize() use.
    SwGuiCond               SetWindowCollapsedAllowFlags : 8;   // store acceptable condition flags for SetNextWindowCollapsed() use.
    SwGuiCond               SetWindowDockAllowFlags : 8;        // store acceptable condition flags for SetNextWindowDock() use.
    SwVec2                  SetWindowPosVal;                    // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)
    SwVec2                  SetWindowPosPivot;                  // store window pivot for positioning. SwVec2(0, 0) when positioning from top-left corner; SwVec2(0.5f, 0.5f) for centering; SwVec2(1, 1) for bottom right.

    SwVector<SwGuiID>       IDStack;                            // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)
    SwGuiWindowTempData     DC;                                 // Temporary per-window data, reset at the beginning of the frame. This used to be called SwGuiDrawContext, hence the "DC" variable name.

    // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer.
    // The main 'OuterRect', omitted as a field, is window->Rect().
    SwRect                  OuterRectClipped;                   // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.
    SwRect                  InnerRect;                          // Inner rectangle (omit title bar, menu bar, scroll bar)
    SwRect                  InnerClipRect;                      // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.
    SwRect                  WorkRect;                           // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).
    SwRect                  ParentWorkRect;                     // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack?
    SwRect                  ClipRect;                           // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().
    SwRect                  ContentRegionRect;                  // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.
    SwVec2ih                HitTestHoleSize;                    // Define an optional rectangular hole where mouse will pass-through the window.
    SwVec2ih                HitTestHoleOffset;

    int                     LastFrameActive;                    // Last frame number the window was Active.
    int                     LastFrameJustFocused;               // Last frame number the window was made Focused.
    float                   LastTimeActive;                     // Last timestamp the window was Active (using float as we don't need high precision there)
    float                   ItemWidthDefault;
    SwGuiStorage            StateStorage;
    SwVector<SwGuiOldColumns> ColumnsStorage;
    float                     FontWindowScale;                    // User scale multiplier per-window, via SetWindowFontScale()
    float                     FontDpiScale;
    int                       SettingsOffset;                     // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)

    SwDrawList*             DrawList;                           // == &DrawListInst (for backward compatibility reason with code using mgui_internal.h we keep this a pointer)
    SwDrawList              DrawListInst;
    SwGuiWindow*            ParentWindow;                       // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.
    SwGuiWindow*            ParentWindowInBeginStack;
    SwGuiWindow*            RootWindow;                         // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.
    SwGuiWindow*            RootWindowPopupTree;                // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
    SwGuiWindow*            RootWindowDockTree;                 // Point to ourself or first ancestor that is not a child window. Cross through dock nodes.
    SwGuiWindow*            RootWindowForTitleBarHighlight;     // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
    SwGuiWindow*            RootWindowForNav;                   // Point to ourself or first ancestor which doesn't have the NavFlattened flag.

    SwGuiWindow*            NavLastChildNavWindow;              // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
    SwGuiID                 NavLastIds[SwGuiNavLayer_COUNT];    // Last known NavId for this window, per layer (0/1)
    SwRect                  NavRectRel[SwGuiNavLayer_COUNT];    // Reference rectangle, in window relative space
    SwVec2                  NavPreferredScoringPosRel[SwGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.
    SwGuiID                 NavRootFocusScopeId;                // Focus Scope ID at the time of Begin()

    int                     MemoryDrawListIdxCapacity;          // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
    int                     MemoryDrawListVtxCapacity;
    bool                    MemoryCompacted;                    // Set when window extraneous data have been garbage collected

    // Docking
    bool                    DockIsActive : 1;             // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1).
    bool                    DockNodeIsVisible : 1;
    bool                    DockTabIsVisible : 1;             // Is our window visible this frame? ~~ is the corresponding tab selected?
    bool                    DockTabWantClose : 1;
    short                   DockOrder;                          // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.
    SwGuiWindowDockStyle    DockStyle;
    SwGuiDockNode*          DockNode;                           // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden.
    SwGuiDockNode*          DockNodeAsHost;                     // Which node are we owning (for parent windows)
    SwGuiID                 DockId;                             // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more
    SwGuiItemStatusFlags    DockTabItemStatusFlags;
    SwRect                  DockTabItemRect;

public:
    SwGuiWindow(SwGuiContext* context, const char* name);
    ~SwGuiWindow();

    SwGuiID                 GetID(const char* str, const char* str_end = NULL);
    SwGuiID                 GetID(const void* ptr);
    SwGuiID                 GetID(int n);
    SwGuiID                 GetIDNoKeepAlive(const char* str, const char* str_end = NULL);
    SwGuiID                 GetIDNoKeepAlive(const void* ptr);
    SwGuiID                 GetIDNoKeepAlive(int n);
    SwGuiID                 GetIDFromRectangle(const SwRect& r_abs);

    // We don't use g.FontSize because the window may be != g.CurrentWindow.
    SwRect                  Rect() const { return SwRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
    float                   CalcFontSize() const { SwGuiContext& g = *GMUI_Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
    float                   TitleBarHeight() const { SwGuiContext& g = *GMUI_Ctx; return (Flags & SwGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
    SwRect                  TitleBarRect() const { return SwRect(Pos, SwVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
    float                   MenuBarHeight() const { SwGuiContext& g = *GMUI_Ctx; return (Flags & SwGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
    SwRect                  MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return SwRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
};


//-----------------------------------------------------------------------------
// [SECTION] Tab bar, Tab item support
//-----------------------------------------------------------------------------

// Extend SwGuiTabBarFlags_
enum SwGuiTabBarFlagsPrivate_
{
    SwGuiTabBarFlags_DockNode = 1 << 20,  // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around]
    SwGuiTabBarFlags_IsFocused = 1 << 21,
    SwGuiTabBarFlags_SaveSettings = 1 << 22,  // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs
};

// Extend SwGuiTabItemFlags_
enum SwGuiTabItemFlagsPrivate_
{
    SwGuiTabItemFlags_SectionMask_ = SwGuiTabItemFlags_Leading | SwGuiTabItemFlags_Trailing,
    SwGuiTabItemFlags_NoCloseButton = 1 << 20,  // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
    SwGuiTabItemFlags_Button = 1 << 21,  // Used by TabItemButton, change the tab item behavior to mimic a button
    SwGuiTabItemFlags_Unsorted = 1 << 22,  // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window.
    SwGuiTabItemFlags_Preview = 1 << 23,  // [Docking] Display tab shape for docking preview (height is adjusted slightly to compensate for the yet missing tab bar)
};

// Storage for one active tab item (sizeof() 48 bytes)
struct SwGuiTabItem
{
    SwGuiID               ID;
    SwGuiTabItemFlags     Flags;
    SwGuiWindow*          Window;                 // When TabItem is part of a DockNode's TabBar, we hold on to a window.
    int                   LastFrameVisible;
    int                   LastFrameSelected;      // This allows us to infer an ordered list of the last activated tabs with little maintenance
    float                 Offset;                 // Position relative to beginning of tab
    float                 Width;                  // Width currently displayed
    float                 ContentWidth;           // Width of label, stored during BeginTabItem() call
    float                 RequestedWidth;         // Width optionally requested by caller, -1.0f is unused
    SwS32                 NameOffset;             // When Window==NULL, offset to name within parent SwGuiTabBar::TabsNames
    SwS16                 BeginOrder;             // BeginTabItem() order, used to re-order tabs after toggling SwGuiTabBarFlags_Reorderable
    SwS16                 IndexDuringLayout;      // Index only used during TabBarLayout()
    bool                  WantClose;              // Marked as closed by SetTabItemClosed()

    SwGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }
};

// Storage for a tab bar (sizeof() 152 bytes)
struct SW_API SwGuiTabBar
{
    SwVector<SwGuiTabItem>      Tabs;
    SwGuiTabBarFlags            Flags;
    SwGuiID                     ID;                     // Zero for tab-bars used by docking
    SwGuiID                     SelectedTabId;          // Selected tab/window
    SwGuiID                     NextSelectedTabId;      // Next selected tab/window. Will also trigger a scrolling animation
    SwGuiID                     VisibleTabId;           // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview)
    int                         CurrFrameVisible;
    int                         PrevFrameVisible;
    SwRect                      BarRect;
    float                       CurrTabsContentsHeight;
    float                       PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar
    float                       WidthAllTabs;           // Actual width of all tabs (locked during layout)
    float                       WidthAllTabsIdeal;      // Ideal width if all tabs were visible and not clipped
    float                       ScrollingAnim;
    float                       ScrollingTarget;
    float                       ScrollingTargetDistToVisibility;
    float                       ScrollingSpeed;
    float                       ScrollingRectMinX;
    float                       ScrollingRectMaxX;
    SwGuiID                     ReorderRequestTabId;
    SwS16                       ReorderRequestOffset;
    SwS8                        BeginCount;
    bool                        WantLayout;
    bool                        VisibleTabWasSubmitted;
    bool                        TabsAddedNew;           // Set to true when a new tab item or button has been added to the tab bar during last frame
    SwS16                       TabsActiveCount;        // Number of tabs submitted this frame.
    SwS16                       LastTabItemIdx;         // Index of last BeginTabItem() tab for use by EndTabItem()
    float                       ItemSpacingY;
    SwVec2                      FramePadding;           // style.FramePadding locked at the time of BeginTabBar()
    SwVec2                      BackupCursorPos;
    SwGuiTextBuffer             TabsNames;              // For non-docking tab bar we re-append names in a contiguous buffer.

    SwGuiTabBar();
    int                         GetTabOrder(const SwGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); }
    const char*                 GetTabName(const SwGuiTabItem* tab) const
    {
        if (tab->Window)
            return tab->Window->Name;
        SW_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size);
        return TabsNames.Buf.Data + tab->NameOffset;
    }
};

//-----------------------------------------------------------------------------
// [SECTION] Table support
//-----------------------------------------------------------------------------

#define SW_COL32_DISABLE                SW_COL32(0,0,0,1)   // Special sentinel code which cannot be used as a regular color.
#define SW_TABLE_MAX_COLUMNS         64                  // sizeof(SwU64) * 8. This is solely because we frequently encode columns set in a SwU64.
#define SW_TABLE_MAX_DRAW_CHANNELS   (4 + 64 * 2)        // See TableSetupDrawChannels()

// Our current column maximum is 64 but we may raise that in the future.
typedef SwS8 SwGuiTableColumnIdx;
typedef SwU8 SwGuiTableDrawChannelIdx;

// [Internal] sizeof() ~ 104
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping.
// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped".
struct SwGuiTableColumn
{
    SwGuiTableColumnFlags      Flags;                          // Flags after some patching (not directly same as provided by user). See SwGuiTableColumnFlags_
    float                      WidthGiven;                     // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space.
    float                      MinX;                           // Absolute positions
    float                      MaxX;
    float                      WidthRequest;                   // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()
    float                      WidthAuto;                      // Automatic width
    float                      StretchWeight;                  // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
    float                      InitStretchWeightOrWidth;       // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).
    SwRect                     ClipRect;                       // Clipping rectangle for the column
    SwGuiID                    UserID;                         // Optional, value passed to TableSetupColumn()
    float                      WorkMinX;                       // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column
    float                      WorkMaxX;                       // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2)
    float                      ItemWidth;                      // Current item width for the column, preserved across rows
    float                      ContentMaxXFrozen;              // Contents maximum position for frozen rows (apart from headers), from which we can infer content width.
    float                      ContentMaxXUnfrozen;
    float                      ContentMaxXHeadersUsed;         // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls
    float                      ContentMaxXHeadersIdeal;
    SwS16                      NameOffset;                     // Offset into parent ColumnsNames[]
    SwGuiTableColumnIdx        DisplayOrder;                   // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)
    SwGuiTableColumnIdx        IndexWithinEnabledSet;          // Index within enabled/visible set (<= IndexToDisplayOrder)
    SwGuiTableColumnIdx        PrevEnabledColumn;              // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column
    SwGuiTableColumnIdx        NextEnabledColumn;              // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column
    SwGuiTableColumnIdx        SortOrder;                      // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort
    SwGuiTableDrawChannelIdx   DrawChannelCurrent;            // Index within DrawSplitter.Channels[]
    SwGuiTableDrawChannelIdx   DrawChannelFrozen;             // Draw channels for frozen rows (often headers)
    SwGuiTableDrawChannelIdx   DrawChannelUnfrozen;           // Draw channels for unfrozen rows
    bool                       IsEnabled;                      // IsUserEnabled && (Flags & SwGuiTableColumnFlags_Disabled) == 0
    bool                       IsUserEnabled;                  // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling).
    bool                       IsUserEnabledNextFrame;
    bool                       IsVisibleX;                     // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled).
    bool                       IsVisibleY;
    bool                       IsRequestOutput;                // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.
    bool                       IsSkipItems;                    // Do we want item submissions to this column to be completely ignored (no layout will happen).
    bool                       IsPreserveWidthAuto;
    SwS8                       NavLayerCurrent;                // SwGuiNavLayer in 1 byte
    SwU8                       AutoFitQueue;                   // Queue of 8 values for the next 8 frames to request auto-fit
    SwU8                       CannotSkipItemsQueue;           // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem
    SwU8                       SortDirection : 2;              // SwGuiSortDirection_Ascending or SwGuiSortDirection_Descending
    SwU8                       SortDirectionsAvailCount : 2;   // Number of available sort directions (0 to 3)
    SwU8                       SortDirectionsAvailMask : 4;    // Mask of available sort directions (1-bit each)
    SwU8                       SortDirectionsAvailList;        // Ordered of available sort directions (2-bits each)

    SwGuiTableColumn()
    {
        memset(this, 0, sizeof(*this));
        StretchWeight = WidthRequest = -1.0f;
        NameOffset = -1;
        DisplayOrder = IndexWithinEnabledSet = -1;
        PrevEnabledColumn = NextEnabledColumn = -1;
        SortOrder = -1;
        SortDirection = SwGuiSortDirection_None;
        DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (SwU8)-1;
    }
};

// Transient cell data stored per row.
// sizeof() ~ 6
struct SwGuiTableCellData
{
    SwU32                         BgColor;    // Actual color
    SwGuiTableColumnIdx           Column;     // Column number
};

// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs, does that needs they could be moved to SwGuiTableTempData ?)
struct SwGuiTableInstanceData
{
    float                           LastOuterHeight;            // Outer height from last frame // FIXME: multi-instance issue (#3955)
    float                           LastFirstRowHeight;         // Height of first row from last frame // FIXME: possible multi-instance issue?

    SwGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = 0.0f; }
};

// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData
struct SW_API SwGuiTable
{
    SwGuiID                       ID;
    SwGuiTableFlags               Flags;
    void*                         RawData;                    // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[]
    SwGuiTableTempData*           TempData;                   // Transient data while table is active. Point within g.CurrentTableStack[]
    SwSpan<SwGuiTableColumn>      Columns;                    // Point within RawData[]
    SwSpan<SwGuiTableColumnIdx>   DisplayOrderToIndex;        // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
    SwSpan<SwGuiTableCellData>    RowCellData;                // Point within RawData[]. Store cells background requests for current row.
    SwU64                         EnabledMaskByDisplayOrder;  // Column DisplayOrder -> IsEnabled map
    SwU64                         EnabledMaskByIndex;         // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
    SwU64                         VisibleMaskByIndex;         // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)
    SwU64                         RequestOutputMaskByIndex;   // Column Index -> IsVisible || AutoFit (== expect user to submit items)
    SwGuiTableFlags               SettingsLoadedFlags;        // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
    int                           SettingsOffset;             // Offset in g.SettingsTables
    int                           LastFrameActive;
    int                           ColumnsCount;               // Number of columns declared in BeginTable()
    int                           CurrentRow;
    int                           CurrentColumn;
    SwS16                         InstanceCurrent;            // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched.
    SwS16                         InstanceInteracted;         // Mark which instance (generally 0) of the same ID is being interacted with
    float                         RowPosY1;
    float                         RowPosY2;
    float                         RowMinHeight;               // Height submitted to TableNextRow()
    float                         RowTextBaseline;
    float                         RowIndentOffsetX;
    SwGuiTableRowFlags            RowFlags : 16;              // Current row flags, see SwGuiTableRowFlags_
    SwGuiTableRowFlags            LastRowFlags : 16;
    int                           RowBgColorCounter;          // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this.
    SwU32                         RowBgColor[2];              // Background color override for current row.
    SwU32                         BorderColorStrong;
    SwU32                         BorderColorLight;
    float                         BorderX1;
    float                         BorderX2;
    float                         HostIndentX;
    float                         MinColumnWidth;
    float                         OuterPaddingX;
    float                         CellPaddingX;               // Padding from each borders
    float                         CellPaddingY;
    float                         CellSpacingX1;              // Spacing between non-bordered cells
    float                         CellSpacingX2;
    float                         InnerWidth;                 // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
    float                         ColumnsGivenWidth;          // Sum of current column width
    float                         ColumnsAutoFitWidth;        // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
    float                         ColumnsStretchSumWeights;   // Sum of weight of all enabled stretching columns
    float                         ResizedColumnNextWidth;
    float                         ResizeLockMinContentsX2;    // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.
    float                         RefScale;                   // Reference scale to be able to rescale columns on font/dpi changes.
    SwRect                        OuterRect;                  // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
    SwRect                        InnerRect;                  // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is
    SwRect                        WorkRect;
    SwRect                        InnerClipRect;
    SwRect                        BgClipRect;                 // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries
    SwRect                        Bg0ClipRectForDrawCmd;      // Actual SwDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped
    SwRect                        Bg2ClipRectForDrawCmd;      // Actual SwDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.
    SwRect                        HostClipRect;               // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
    SwRect                        HostBackupInnerClipRect;    // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()
    SwGuiWindow*                  OuterWindow;                // Parent window for the table
    SwGuiWindow*                  InnerWindow;                // Window holding the table data (== OuterWindow or a child window)
    SwGuiTextBuffer               ColumnsNames;               // Contiguous buffer holding columns names
    SwDrawListSplitter*           DrawSplitter;               // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly
    SwGuiTableInstanceData        InstanceDataFirst;
    SwVector<SwGuiTableInstanceData>    InstanceDataExtra;  // FIXME-OPT: Using a small-vector pattern would be good.
    SwGuiTableColumnSortSpecs     SortSpecsSingle;
    SwVector<SwGuiTableColumnSortSpecs> SortSpecsMulti;     // FIXME-OPT: Using a small-vector pattern would be good.
    SwGuiTableSortSpecs           SortSpecs;                  // Public facing sorts specs, this is what we return in TableGetSortSpecs()
    SwGuiTableColumnIdx           SortSpecsCount;
    SwGuiTableColumnIdx           ColumnsEnabledCount;        // Number of enabled columns (<= ColumnsCount)
    SwGuiTableColumnIdx           ColumnsEnabledFixedCount;   // Number of enabled columns (<= ColumnsCount)
    SwGuiTableColumnIdx           DeclColumnsCount;           // Count calls to TableSetupColumn()
    SwGuiTableColumnIdx           HoveredColumnBody;          // Index of column whose visible region is being hovered. Swportant: == ColumnsCount when hovering empty region after the right-most column!
    SwGuiTableColumnIdx           HoveredColumnBorder;        // Index of column whose right-border is being hovered (for resizing).
    SwGuiTableColumnIdx           AutoFitSingleColumn;        // Index of single column requesting auto-fit.
    SwGuiTableColumnIdx           ResizedColumn;              // Index of column being resized. Reset when InstanceCurrent==0.
    SwGuiTableColumnIdx           LastResizedColumn;          // Index of column being resized from previous frame.
    SwGuiTableColumnIdx           HeldHeaderColumn;           // Index of column header being held.
    SwGuiTableColumnIdx           ReorderColumn;              // Index of column being reordered. (not cleared)
    SwGuiTableColumnIdx           ReorderColumnDir;           // -1 or +1
    SwGuiTableColumnIdx           LeftMostEnabledColumn;      // Index of left-most non-hidden column.
    SwGuiTableColumnIdx           RightMostEnabledColumn;     // Index of right-most non-hidden column.
    SwGuiTableColumnIdx           LeftMostStretchedColumn;    // Index of left-most stretched column.
    SwGuiTableColumnIdx           RightMostStretchedColumn;   // Index of right-most stretched column.
    SwGuiTableColumnIdx           ContextPopupColumn;         // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot
    SwGuiTableColumnIdx           FreezeRowsRequest;          // Requested frozen rows count
    SwGuiTableColumnIdx           FreezeRowsCount;            // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)
    SwGuiTableColumnIdx           FreezeColumnsRequest;       // Requested frozen columns count
    SwGuiTableColumnIdx           FreezeColumnsCount;         // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)
    SwGuiTableColumnIdx           RowCellDataCurrent;         // Index of current RowCellData[] entry in current row
    SwGuiTableDrawChannelIdx      DummyDrawChannel;           // Redirect non-visible columns here.
    SwGuiTableDrawChannelIdx      Bg2DrawChannelCurrent;      // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[]
    SwGuiTableDrawChannelIdx      Bg2DrawChannelUnfrozen;
    bool                          IsLayoutLocked;             // Set by TableUpdateLayout() which is called when beginning the first row.
    bool                          IsInsideRow;                // Set when inside TableBeginRow()/TableEndRow().
    bool                          IsInitializing;
    bool                          IsSortSpecsDirty;
    bool                          IsUsingHeaders;             // Set when the first row had the SwGuiTableRowFlags_Headers flag.
    bool                          IsContextPopupOpen;         // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
    bool                          IsSettingsRequestLoad;
    bool                          IsSettingsDirty;            // Set when table settings have changed and needs to be reported into SwGuiTableSetttings data.
    bool                          IsDefaultDisplayOrder;      // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
    bool                          IsResetAllRequest;
    bool                          IsResetDisplayOrderRequest;
    bool                          IsUnfrozenRows;             // Set when we got past the frozen row.
    bool                          IsDefaultSizingPolicy;      // Set if user didn't explicitly set a sizing policy in BeginTable()
    bool                          MemoryCompacted;
    bool                          HostSkipItems;              // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis

    SwGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; }
    ~SwGuiTable() { SW_FREE(RawData); }
};

// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
struct SW_API SwGuiTableTempData
{
    int                             TableIndex;                 // Index in g.Tables.Buf[] pool
    float                           LastTimeActive;             // Last timestamp this structure was used

    SwVec2                          UserOuterSize;              // outer_size.x passed to BeginTable()
    SwDrawListSplitter              DrawSplitter;

    SwRect                          HostBackupWorkRect;         // Backup of InnerWindow->WorkRect at the end of BeginTable()
    SwRect                          HostBackupParentWorkRect;   // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()
    SwVec2                          HostBackupPrevLineSize;     // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()
    SwVec2                          HostBackupCurrLineSize;     // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()
    SwVec2                          HostBackupCursorMaxPos;     // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
    SwVec1                          HostBackupColumnsOffset;    // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()
    float                           HostBackupItemWidth;        // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()
    int                             HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()

    SwGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; }
};

// sizeof() ~ 12
struct SwGuiTableColumnSettings
{
    float                           WidthOrWeight;
    SwGuiID                         UserID;
    SwGuiTableColumnIdx             Index;
    SwGuiTableColumnIdx             DisplayOrder;
    SwGuiTableColumnIdx             SortOrder;
    SwU8                            SortDirection : 2;
    SwU8                            IsEnabled : 1; // "Visible" in ini file
    SwU8                            IsStretch : 1;

    SwGuiTableColumnSettings()
    {
        WidthOrWeight = 0.0f;
        UserID = 0;
        Index = -1;
        DisplayOrder = SortOrder = -1;
        SortDirection = SwGuiSortDirection_None;
        IsEnabled = 1;
        IsStretch = 0;
    }
};

// This is designed to be stored in a single SwChunkStream (1 header followed by N SwGuiTableColumnSettings, etc.)
struct SwGuiTableSettings
{
    SwGuiID                         ID;                     // Set to 0 to invalidate/delete the setting
    SwGuiTableFlags                 SaveFlags;              // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)
    float                           RefScale;               // Reference scale to be able to rescale columns on font/dpi changes.
    SwGuiTableColumnIdx             ColumnsCount;
    SwGuiTableColumnIdx             ColumnsCountMax;        // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher
    bool                            WantApply;              // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)

    SwGuiTableSettings() { memset(this, 0, sizeof(*this)); }
    SwGuiTableColumnSettings* GetColumnSettings() { return (SwGuiTableColumnSettings*)(this + 1); }
};

//-----------------------------------------------------------------------------
// [SECTION] SwGui internal API
// No guarantee of forward compatibility here!
//-----------------------------------------------------------------------------

namespace SW
{
    // Windows
    // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
    // If this ever crash because g.CurrentWindow is NULL it means that either
    // - SW::NewFrame() has never been called, which is illegal.
    // - You are calling SW functions after SW::EndFrame()/SW::Render() and before the next SW::NewFrame(), which is also illegal.
    inline    SwGuiWindow*            GetCurrentWindowRead() { SwGuiContext& g = *GMUI_Ctx; return g.CurrentWindow; }
    inline    SwGuiWindow*            GetCurrentWindow() { SwGuiContext& g = *GMUI_Ctx; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
    inline    SwRect                  WindowRectAbsToRel(SwGuiWindow* window, const SwRect& r) { SwVec2 off = window->DC.CursorStartPos; return SwRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
    inline    SwRect                  WindowRectRelToAbs(SwGuiWindow* window, const SwRect& r) { SwVec2 off = window->DC.CursorStartPos; return SwRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
    SW_API  SwGuiWindow*              FindWindowByID(SwGuiID id);
    SW_API  SwGuiWindow*              FindWindowByName(const char* name);
    SW_API void                       UpdateWindowParentAndRootLinks(SwGuiWindow* window, SwGuiWindowFlags flags, SwGuiWindow* parent_window);
    SW_API SwVec2                     CalcWindowNextAutoFitSize(SwGuiWindow* window);
    SW_API bool                       IsWindowChildOf(SwGuiWindow* window, SwGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy);
    SW_API void                       SetWindowPos(SwGuiWindow* window, const SwVec2& pos, SwGuiCond cond = 0);
    SW_API void                       SetWindowSize(SwGuiWindow* window, const SwVec2& size, SwGuiCond cond = 0);
    SW_API void                       SetWindowCollapsed(SwGuiWindow* window, bool collapsed, SwGuiCond cond = 0);                    // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed().
    SW_API void                       SetWindowHitTestHole(SwGuiWindow* window, const SwVec2& pos, const SwVec2& size);
    SW_API bool                       IsWindowWithinBeginStackOf(SwGuiWindow* window, SwGuiWindow* potential_parent);
    SW_API bool                       IsWindowAbove(SwGuiWindow* potential_above, SwGuiWindow* potential_below);
    SW_API bool                       IsWindowNavFocusable(SwGuiWindow* window);

    // Windows: Display Order and Focus Order
    SW_API void                       FocusWindow(SwGuiWindow* window, SwGuiFocusRequestFlags flags = 0);
    SW_API void                       FocusTopMostWindowUnderOne(SwGuiWindow* under_this_window, SwGuiWindow* ignore_window, SwGuiViewport* filter_viewport, SwGuiFocusRequestFlags flags);
    SW_API void                       BringWindowToFocusFront(SwGuiWindow* window);
    SW_API void                       BringWindowToDisplayFront(SwGuiWindow* window);
    SW_API void                       BringWindowToDisplayBehind(SwGuiWindow* window, SwGuiWindow* above_window);
    SW_API int                        FindWindowDisplayIndex(SwGuiWindow* window);
    SW_API SwGuiWindow*               FindBottomMostVisibleWindowWithinBeginStack(SwGuiWindow* window);

    // Fonts, drawing
    SW_API void                       SetCurrentFont(SwFont* font);
    inline SwFont*                    GetDefaultFont() { SwGuiContext& g = *GMUI_Ctx; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
    inline SwDrawList*                GetForegroundDrawList(SwGuiWindow* window) { SW_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
    SW_API SwDrawList*                GetBackgroundDrawList(SwGuiViewport* viewport);                     // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind  imgui contents.
    SW_API SwDrawList*                GetForegroundDrawList(SwGuiViewport* viewport);                     // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over  imgui contents.
    SW_API void                       AddDrawListToDrawDataEx(SwDrawData* draw_data, SwVector<SwDrawList*>* out_list, SwDrawList* draw_list);

    // Init
    SW_API void                       Initialize();
    SW_API void                       Shutdown();

    // NewFrame
    SW_API void                       UpdateInputEvents(bool trickle_fast_inputs);
    SW_API void                       UpdateHoveredWindowAndCaptureFlags();
    SW_API void                       StartMouseMovingWindow(SwGuiWindow* window);
    SW_API void                       StartMouseMovingWindowOrNode(SwGuiWindow* window, SwGuiDockNode* node, bool undock_floating_node);
    SW_API void                       UpdateMouseMovingWindowNewFrame();
    SW_API void                       UpdateMouseMovingWindowEndFrame();

    // Viewports
    SW_API void                       TranslateWindowsInViewport(SwGuiViewportP* viewport, const SwVec2& old_pos, const SwVec2& new_pos);
    SW_API void                       ScaleWindowsInViewport(SwGuiViewportP* viewport, float scale);
    SW_API void                       SetWindowViewport(SwGuiWindow* window, SwGuiViewportP* viewport);
    SW_API void                       SetCurrentViewport(SwGuiWindow* window, SwGuiViewportP* viewport);
    SW_API void                       DestroyPlatformWindow(SwGuiViewportP* viewport);
    SW_API const SwGuiPlatformMonitor* GetViewportPlatformMonitor(SwGuiViewport* viewport);
    SW_API SwGuiViewportP*            FindHoveredViewportFromPlatformWindowStack(const SwVec2& mouse_platform_pos);

    // Settings
    SW_API void                       MarkIniSettingsDirty();
    SW_API void                       MarkIniSettingsDirty(SwGuiWindow* window);
    SW_API void                       AddSettingsHandler(const SwGuiSettingsHandler* handler);
    SW_API SwGuiWindowSettings*       FindOrCreateWindowSettings(const char* name);
    SW_API void                       RemoveSettingsHandler(const char* type_name);
    SW_API SwGuiSettingsHandler*      FindSettingsHandler(const char* type_name);
    SW_API SwGuiWindowSettings*       FindWindowSettings(SwGuiID id);

    // Settings - Windows
    SW_API SwGuiWindowSettings*       CreateNewWindowSettings(const char* name);
    SW_API SwGuiWindowSettings*       FindWindowSettingsByID(SwGuiID id);
    SW_API SwGuiWindowSettings*       FindWindowSettingsByWindow(SwGuiWindow* window);
    SW_API void                       ClearWindowSettings(const char* name);

    // Combos
    SW_API bool                       BeginComboPopup(SwGuiID popup_id, const SwRect& bb, SwGuiComboFlags flags);
    SW_API bool                       BeginComboPreview();
    SW_API void                       EndComboPreview();

    // Gamepad/Keyboard Navigation
    SW_API void                       NavInitWindow(SwGuiWindow* window, bool force_reinit);
    SW_API void                       NavInitRequestApplyResult();
    SW_API void                       NavMoveRequestResolveWithLastItem(SwGuiNavItemData* result);
    SW_API bool                       NavMoveRequestButNoResultYet();
    SW_API void                       NavMoveRequestCancel();
    SW_API void                       NavMoveRequestApplyResult();
    SW_API void                       NavMoveRequestForward(SwGuiDir move_dir, SwGuiDir clip_dir, SwGuiNavMoveFlags move_flags, SwGuiScrollFlags scroll_flags);
    SW_API void                       NavMoveRequestTryWrapping(SwGuiWindow* window, SwGuiNavMoveFlags move_flags);
    SW_API void                       NavMoveRequestSubmit(SwGuiDir move_dir, SwGuiDir clip_dir, SwGuiNavMoveFlags move_flags, SwGuiScrollFlags scroll_flags);
    SW_API void                       SetNavWindow(SwGuiWindow* window);
    SW_API void                       SetNavID(SwGuiID id, SwGuiNavLayer nav_layer, SwGuiID focus_scope_id, const SwRect& rect_rel);

    // Inputs
    // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
    inline bool                       IsNamedKey(SwGuiKey key) { return key >= SwGuiKey_NamedKey_BEGIN && key < SwGuiKey_NamedKey_END; }
    inline bool                       IsNamedKeyOrModKey(SwGuiKey key) { return (key >= SwGuiKey_NamedKey_BEGIN && key < SwGuiKey_NamedKey_END) || key == SwGuiMod_Ctrl || key == SwGuiMod_Shift || key == SwGuiMod_Alt || key == SwGuiMod_Super; }
    inline bool                       IsLegacyKey(SwGuiKey key) { return key >= SwGuiKey_LegacyNativeKey_BEGIN && key < SwGuiKey_LegacyNativeKey_END; }
    inline bool                       IsGamepadKey(SwGuiKey key) { return key >= SwGuiKey_Gamepad_BEGIN && key < SwGuiKey_Gamepad_END; }
    inline bool                       IsAliasKey(SwGuiKey key) { return key >= SwGuiKey_Aliases_BEGIN && key < SwGuiKey_Aliases_END; }
    inline SwGuiKeyChord              ConvertShortcutMod(SwGuiKeyChord key_chord) { SwGuiContext& g = *GMUI_Ctx; SW_ASSERT_PARANOID(key_chord & SwGuiMod_Shortcut); return (key_chord & ~SwGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? SwGuiMod_Super : SwGuiMod_Ctrl); }
    inline SwGuiKey                   ConvertSingleModFlagToKey(SwGuiContext* ctx, SwGuiKey key)
    {
        SwGuiContext& g = *ctx;
        if (key == SwGuiMod_Ctrl) return SwGuiKey_ReservedForModCtrl;
        if (key == SwGuiMod_Shift) return SwGuiKey_ReservedForModShift;
        if (key == SwGuiMod_Alt) return SwGuiKey_ReservedForModAlt;
        if (key == SwGuiMod_Super) return SwGuiKey_ReservedForModSuper;
        if (key == SwGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? SwGuiKey_ReservedForModSuper : SwGuiKey_ReservedForModCtrl);
        return key;
    }

    inline bool                         IsActiveIdUsingNavDir(SwGuiDir dir) { SwGuiContext& g = *GMUI_Ctx; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
    inline bool                         IsActiveIdUsingKey(SwGuiKey key) { SwGuiContext& g = *GMUI_Ctx; return g.ActiveIdUsingKeyInputMask[key]; }
    inline void                         SetActiveIdUsingKey(SwGuiKey key) { SwGuiContext& g = *GMUI_Ctx; g.ActiveIdUsingKeyInputMask.SetBit(key); }
    inline SwGuiKey                     MouseButtonToKey(SwGuiMouseButton button) { SW_ASSERT(button >= 0 && button < SwGuiMouseButton_COUNT); return (SwGuiKey)(SwGuiKey_MouseLeft + button); }
    SW_API bool                         IsMouseDragPastThreshold(SwGuiMouseButton button, float lock_threshold = -1.0f);
    SW_API SwVec2                       GetKeyMagnitude2d(SwGuiKey key_left, SwGuiKey key_right, SwGuiKey key_up, SwGuiKey key_down);
    SW_API float                        GetNavTweakPressedAmount(SwGuiAxis axis);
    SW_API int                          CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
    SW_API SwGuiKeyData*                GetKeyData(SwGuiContext* ctx, SwGuiKey key);
    inline SwGuiKeyData*                GetKeyData(SwGuiKey key) { SwGuiContext& g = *GMUI_Ctx; return GetKeyData(&g, key); }
    SW_API SwGuiModFlags                GetMergedModFlags();
    SW_API SwVec2                       GetKeyVector2d(SwGuiKey key_left, SwGuiKey key_right, SwGuiKey key_up, SwGuiKey key_down);
    SW_API void                         SetActiveIdUsingAllKeyboardKeys();
    SW_API void                         GetTypematicRepeatRate(SwGuiInputFlags flags, float* repeat_delay, float* repeat_rate);

    // [EXPERIMENTAL] Low-Level: Key/Input Ownership
    // - The idea is that instead of "eating" a given input, we can link to an owner id.
    // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).
    // - Input queries can then read input by specifying SwGuiKeyOwner_Any (== 0), SwGuiKeyOwner_None (== -1) or a custom ID.
    // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using SwGuiKeyOwner_Any (== 0).
    // - Input ownership is automatically released on the frame after a key is released. Therefore:
    //   - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).
    //   - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).
    // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.
    // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.
    //   Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.
    SW_API SwGuiID                    GetKeyOwner(SwGuiKey key);
    SW_API void                       SetKeyOwner(SwGuiKey key, SwGuiID owner_id, SwGuiInputFlags flags = 0);
    SW_API void                       SetKeyOwnersForKeyChord(SwGuiKeyChord key, SwGuiID owner_id, SwGuiInputFlags flags = 0);
    SW_API void                       SetItemKeyOwner(SwGuiKey key, SwGuiInputFlags flags = 0);           // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
    SW_API bool                       TestKeyOwner(SwGuiKey key, SwGuiID owner_id);                       // Test that key is either not owned, either owned by 'owner_id'
    inline SwGuiKeyOwnerData*         GetKeyOwnerData(SwGuiContext* ctx, SwGuiKey key) { if (key & SwGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); SW_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - SwGuiKey_NamedKey_BEGIN]; }

    // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership
    // - Important: legacy IsKeyPressed(SwGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ SwGuiInputFlags_Repeat flag.
    // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0)
    // - Specifying a value for 'SwGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.
    //   Legacy functions use SwGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used SwGuiInputFlags_LockThisFrame or SwGuiInputFlags_LockUntilRelease.
    // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.
    SW_API bool                       IsKeyDown(SwGuiKey key, SwGuiID owner_id);
    SW_API bool                       IsKeyPressed(SwGuiKey key, SwGuiID owner_id, SwGuiInputFlags flags = 0);    // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit SwGuiInputFlags_Repeat.
    SW_API bool                       IsKeyReleased(SwGuiKey key, SwGuiID owner_id);
    SW_API bool                       IsMouseDown(SwGuiMouseButton button, SwGuiID owner_id);
    SW_API bool                       IsMouseClicked(SwGuiMouseButton button, SwGuiID owner_id, SwGuiInputFlags flags = 0);
    SW_API bool                       IsMouseReleased(SwGuiMouseButton button, SwGuiID owner_id);
    SW_API bool                       IsMouseDoubleClicked(SwGuiMouseButton button, SwGuiID owner_id);

    // [EXPERIMENTAL] Shortcut Routing
    // - SwGuiKeyChord = a SwGuiKey optionally OR-red with SwGuiMod_Alt/SwGuiMod_Ctrl/SwGuiMod_Shift/SwGuiMod_Super.
    //     SwGuiKey_C                 (accepted by functions taking SwGuiKey or SwGuiKeyChord)
    //     SwGuiKey_C | SwGuiMod_Ctrl (accepted by functions taking SwGuiKeyChord)
    //   ONLY SwGuiMod_XXX values are legal to 'OR' with an SwGuiKey. You CANNOT 'OR' two SwGuiKey values.
    // - When using one of the routing flags (e.g. SwGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy.
    // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
    // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route.
    // - Multiple read sites may use the same owner id and will all get the granted route.
    // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location.
    // - TL;DR;
    //   - IsKeyChordPressed() compares mods + call IsKeyPressed() -> function has no side-effect.
    //   - Shortcut() submits a route then if currently can be routed calls IsKeyChordPressed() -> function has (desirable) side-effects.
    SW_API bool                       IsKeyChordPressed(SwGuiKeyChord key_chord, SwGuiID owner_id, SwGuiInputFlags flags = 0);
    SW_API bool                       Shortcut(SwGuiKeyChord key_chord, SwGuiID owner_id = 0, SwGuiInputFlags flags = 0);
    SW_API bool                       SetShortcutRouting(SwGuiKeyChord key_chord, SwGuiID owner_id = 0, SwGuiInputFlags flags = 0);
    SW_API bool                       TestShortcutRouting(SwGuiKeyChord key_chord, SwGuiID owner_id);
    SW_API SwGuiKeyRoutingData*       GetShortcutRoutingData(SwGuiKeyChord key_chord);
    
    // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
    SW_API void                       EndColumns();
    SW_API float                      GetColumnOffsetFromNorm(const SwGuiOldColumns* columns, float offset_norm);
    SW_API float                      GetColumnNormFromOffset(const SwGuiOldColumns* columns, float offset);
    SW_API void                       SetWindowClipRectBeforeSetChannel(SwGuiWindow* window, const SwRect& clip_rect);
    SW_API void                       PopColumnsBackground();
    SW_API void                       PushColumnsBackground();

    // Tables: Internals
    SW_API void                       TableGcCompactTransientBuffers(SwGuiTable* table);
    SW_API void                       TableGcCompactTransientBuffers(SwGuiTableTempData* table);
    SW_API void                       TableGcCompactSettings();

    // Tables: Candidates for public API
    SW_API void                       TableOpenContextMenu(int column_n = -1);
    SW_API void                       TableSetColumnWidth(int column_n, float width);
    SW_API void                       TableSetColumnSortDirection(int column_n, SwGuiSortDirection sort_direction, bool append_to_sort_specs);
    SW_API int                        TableGetHoveredColumn(); // May use (TableGetColumnFlags() & SwGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
    SW_API float                      TableGetHeaderRowHeight();
    SW_API void                       TablePushBackgroundChannel();
    SW_API void                       TablePopBackgroundChannel();

    // Tables: Settings
    SW_API void                       TableSettingsAddSettingsHandler();
    SW_API SwGuiTableSettings*        TableSettingsCreate(SwGuiID id, int columns_count);
    SW_API SwGuiTableSettings*        TableSettingsFindByID(SwGuiID id);

    // Tab Bars
    SW_API bool                       BeginTabBarEx(SwGuiTabBar* tab_bar, const SwRect& bb, SwGuiTabBarFlags flags, SwGuiDockNode* dock_node);
    SW_API SwGuiTabItem*              TabBarFindTabByID(SwGuiTabBar* tab_bar, SwGuiID tab_id);
    SW_API SwGuiTabItem*              TabBarFindMostRecentlySelectedTabForActiveWindow(SwGuiTabBar* tab_bar);
    SW_API const char*                TabBarGetTabName(SwGuiTabBar* tab_bar, SwGuiTabItem* tab);
    SW_API void                       TabBarAddTab(SwGuiTabBar* tab_bar, SwGuiTabItemFlags tab_flags, SwGuiWindow* window);
    SW_API void                       TabBarRemoveTab(SwGuiTabBar* tab_bar, SwGuiID tab_id);
    SW_API void                       TabBarCloseTab(SwGuiTabBar* tab_bar, SwGuiTabItem* tab);
    SW_API void                       TabBarQueueReorder(SwGuiTabBar* tab_bar, const SwGuiTabItem* tab, int offset);
    SW_API void                       TabBarQueueReorderFromMousePos(SwGuiTabBar* tab_bar, const SwGuiTabItem* tab, SwVec2 mouse_pos);
    SW_API bool                       TabBarProcessReorder(SwGuiTabBar* tab_bar);
    SW_API bool                       TabItemEx(SwGuiTabBar* tab_bar, const char* label, bool* p_open, SwGuiTabItemFlags flags, SwGuiWindow* docked_window);
    SW_API SwVec2                     TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker);
    SW_API SwVec2                     TabItemCalcSize(SwGuiWindow* window);
    SW_API void                       TabItemBackground(SwDrawList* draw_list, const SwRect& bb, SwGuiTabItemFlags flags, SwU32 col);
    SW_API void                       TabItemLabelAndCloseButton(SwDrawList* draw_list, const SwRect& bb, SwGuiTabItemFlags flags, SwVec2 frame_padding, const char* label, SwGuiID tab_id, SwGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped);

    // Shade functions (write over already created vertices)
    SW_API void                       ShadeVertsLinearColorGradientKeepAlpha(SwDrawList* draw_list, int vert_start_idx, int vert_end_idx, SwVec2 gradient_p0, SwVec2 gradient_p1, SwU32 col0, SwU32 col1);
    SW_API void                       ShadeVertsLinearUV(SwDrawList* draw_list, int vert_start_idx, int vert_end_idx, const SwVec2& a, const SwVec2& b, const SwVec2& uv_a, const SwVec2& uv_b, bool clamp);

    // Render helpers
    // AVOID USING OUTSIDE OF SW.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
    // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
    SW_API void                       RenderText(SwVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
    SW_API void                       RenderTextWrapped(SwVec2 pos, const char* text, const char* text_end, float wrap_width);
    SW_API void                       RenderFrame(SwVec2 p_min, SwVec2 p_max, SwU32 fill_col, bool border = true, float rounding = 0.0f);
    SW_API void                       RenderFrameBorder(SwVec2 p_min, SwVec2 p_max, float rounding = 0.0f);
    SW_API void                       RenderTextEllipsis(SwDrawList* draw_list, const SwVec2& pos_min, const SwVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const SwVec2* text_size_if_known);
    SW_API void                       RenderMouseCursor(SwVec2 pos, float scale, SwGuiMouseCursor mouse_cursor, SwU32 col_fill, SwU32 col_border, SwU32 col_shadow);
    SW_API void                       RenderColorRectWithAlphaCheckerboard(SwDrawList* draw_list, SwVec2 p_min, SwVec2 p_max, SwU32 fill_col, float grid_step, SwVec2 grid_off, float rounding = 0.0f, SwDrawFlags flags = 0);
    SW_API const char*                FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
    SW_API void                       RenderTextClipped(const SwVec2& pos_min, const SwVec2& pos_max, const char* text, const char* text_end, const SwVec2* text_size_if_known, const SwVec2& align = SwVec2(0, 0), const SwRect* clip_rect = NULL);
    SW_API void                       RenderTextClippedEx(SwDrawList* draw_list, const SwVec2& pos_min, const SwVec2& pos_max, const char* text, const char* text_end, const SwVec2* text_size_if_known, const SwVec2& align = SwVec2(0, 0), const SwRect* clip_rect = NULL);
    SW_API void                       RenderNavHighlight(const SwRect& bb, SwGuiID id, SwGuiNavHighlightFlags flags = SwGuiNavHighlightFlags_TypeDefault); // Navigation highlight

    // Render helpers (those functions don't access any SwGui state!)
    SW_API void                       RenderArrow(SwDrawList* draw_list, SwVec2 pos, SwU32 col, SwGuiDir dir, float scale = 1.0f);
    SW_API void                       RenderBullet(SwDrawList* draw_list, SwVec2 pos, SwU32 col);
    SW_API void                       RenderCheckMark(SwDrawList* draw_list, SwVec2 pos, SwU32 col, float sz);
    SW_API void                       RenderArrowPointingAt(SwDrawList* draw_list, SwVec2 pos, SwVec2 half_sz, SwGuiDir direction, SwU32 col);
    SW_API void                       RenderArrowDockMenu(SwDrawList* draw_list, SwVec2 p_min, float sz, SwU32 col);
    SW_API void                       RenderRectFilledRangeH(SwDrawList* draw_list, const SwRect& rect, SwU32 col, float x_start_norm, float x_end_norm, float rounding);
    SW_API void                       RenderRectFilledWithHole(SwDrawList* draw_list, const SwRect& outer, const SwRect& inner, SwU32 col, float rounding);
    SW_API SwDrawFlags                CalcRoundingFlagsForRectInRect(const SwRect& r_in, const SwRect& r_outer, float threshold);

    // Widgets
    SW_API void                       TextEx(const char* text, const char* text_end = NULL, SwGuiTextFlags flags = 0);
    SW_API bool                       ButtonEx(const char* label, const SwVec2& size_arg = SwVec2(0, 0), SwGuiButtonFlags flags = 0);
    SW_API bool                       CloseButton(SwGuiID id, const SwVec2& pos);
    SW_API bool                       CollapseButton(SwGuiID id, const SwVec2& pos, SwGuiDockNode* dock_node);
    SW_API bool                       ArrowButtonEx(const char* str_id, SwGuiDir dir, SwVec2 size_arg, SwGuiButtonFlags flags = 0);
    SW_API void                       Scrollbar(SwGuiAxis axis);
    SW_API bool                       ScrollbarEx(const SwRect& bb, SwGuiID id, SwGuiAxis axis, SwS64* p_scroll_v, SwS64 avail_v, SwS64 contents_v, SwDrawFlags flags);
    SW_API SwRect                     GetWindowScrollbarRect(SwGuiWindow* window, SwGuiAxis axis);
    SW_API SwGuiID                    GetWindowScrollbarID(SwGuiWindow* window, SwGuiAxis axis);
    SW_API void                       SeparatorEx(SwGuiSeparatorFlags flags);

    // Generic context hooks
    SW_API SwGuiID                    AddContextHook(SwGuiContext* context, const SwGuiContextHook* hook);
    SW_API void                       RemoveContextHook(SwGuiContext* context, SwGuiID hook_to_remove);
    SW_API void                       CallContextHooks(SwGuiContext* context, SwGuiContextHookType type);

    // Scrolling
    SW_API void                       SetNextWindowScroll(const SwVec2& scroll); // Use -1.0f on one axis to leave as-is
    SW_API void                       SetScrollX(SwGuiWindow* window, float scroll_x);
    SW_API void                       SetScrollY(SwGuiWindow* window, float scroll_y);
    SW_API void                       SetScrollFromPosX(SwGuiWindow* window, float local_x, float center_x_ratio);
    SW_API void                       SetScrollFromPosY(SwGuiWindow* window, float local_y, float center_y_ratio);

    // Early work-in-progress API (ScrollToItem() will become public)
    SW_API SwVec2                     ScrollToRectEx(SwGuiWindow* window, const SwRect& rect, SwGuiScrollFlags flags = 0);

    // Basic Accessors
    inline SwGuiID                    GetItemID() { SwGuiContext& g = *GMUI_Ctx; return g.LastItemData.ID; }   // Get ID of last item (~~ often same SW::GetID(label) beforehand)
    inline SwGuiItemStatusFlags       GetItemStatusFlags() { SwGuiContext& g = *GMUI_Ctx; return g.LastItemData.StatusFlags; }
    inline SwGuiItemFlags             GetItemFlags() { SwGuiContext& g = *GMUI_Ctx; return g.LastItemData.InFlags; }
    inline SwGuiID                    GetActiveID() { SwGuiContext& g = *GMUI_Ctx; return g.ActiveId; }
    inline SwGuiID                    GetFocusID() { SwGuiContext& g = *GMUI_Ctx; return g.NavId; }
    SW_API void                       SetActiveID(SwGuiID id, SwGuiWindow* window);
    SW_API void                       SetFocusID(SwGuiID id, SwGuiWindow* window);
    SW_API void                       ClearActiveID();
    SW_API SwGuiID                    GetHoveredID();
    SW_API void                       SetHoveredID(SwGuiID id);
    SW_API void                       KeepAliveID(SwGuiID id);
    SW_API void                       MarkItemEdited(SwGuiID id);     // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function.
    SW_API void                       PushOverrideID(SwGuiID id);     // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes)
    SW_API SwGuiID                    GetIDWithSeed(const char* str_id_begin, const char* str_id_end, SwGuiID seed);

    // Parameter stacks (shared)
    SW_API void                       PushItemFlag(SwGuiItemFlags option, bool enabled);
    SW_API void                       PopItemFlag();

    // Logging/Capture
    SW_API void                       LogRenderedText(const SwVec2* ref_pos, const char* text, const char* text_end = NULL);
    SW_API void                       LogSetNextTextDecoration(const char* prefix, const char* suffix);

    // Popups, Modals, Tooltips
    SW_API bool                       BeginChildEx(const char* name, SwGuiID id, const SwVec2& size_arg, SwGuiChildFlags child_flags, SwGuiWindowFlags window_flags);
    SW_API void                       OpenPopupEx(SwGuiID id, SwGuiPopupFlags popup_flags = SwGuiPopupFlags_None);
    SW_API SwGuiWindow*               GetTopMostPopupModal();
    SW_API SwGuiWindow*               GetTopMostAndVisiblePopupModal();
    SW_API void                       ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
    SW_API void                       ClosePopupsOverWindow(SwGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
    SW_API SwVec2                     FindBestWindowPosForPopup(SwGuiWindow* window);
    SW_API SwVec2                     FindBestWindowPosForPopupEx(const SwVec2& ref_pos, const SwVec2& size, SwGuiDir* last_dir, const SwRect& r_outer, const SwRect& r_avoid, SwGuiPopupPositionPolicy policy);
    SW_API SwRect                     GetPopupAllowedExtentRect(SwGuiWindow* window);
    SW_API bool                       IsPopupOpen(SwGuiID id, SwGuiPopupFlags popup_flags);
    SW_API bool                       BeginPopupEx(SwGuiID id, SwGuiWindowFlags extra_flags);
    SW_API void                       BeginTooltipEx(SwGuiTooltipFlags tooltip_flags, SwGuiWindowFlags extra_window_flags);

    // Menus
    SW_API bool                       BeginViewportSideBar(const char* name, SwGuiViewport* viewport, SwGuiDir dir, float size, SwGuiWindowFlags window_flags);
    SW_API bool                       BeginMenuEx(const char* label, const char* icon, bool enabled = true);
    SW_API bool                       MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true);

    // Basic Helpers for widget code
    SW_API void                       ItemSize(const SwVec2& size, float text_baseline_y = -1.0f);
    inline void                       ItemSize(const SwRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.
    SW_API SwVec2                     CalcItemSize(SwVec2 size, float default_w, float default_h);
    SW_API bool                       ItemAdd(const SwRect& bb, SwGuiID id, const SwRect* nav_bb = NULL, SwGuiItemFlags extra_flags = 0);
    SW_API bool                       ItemHoverable(const SwRect& bb, SwGuiID id, SwGuiItemFlags item_flags);
    SW_API SwVec2                     GetContentRegionMaxAbs();
	SW_API bool                       IsClippedEx(const SwRect& bb, SwGuiID id);
    SW_API void                       SetLastItemData(SwGuiID item_id, SwGuiItemFlags in_flags, SwGuiItemStatusFlags status_flags, const SwRect& item_rect);
    SW_API float                      CalcWrapWidthForPos(const SwVec2& pos, float wrap_pos_x);
    SW_API void                       PushMultiItemsWidths(int components, float width_full);
    SW_API void                       ShrinkWidths(SwGuiShrinkWidthItem* items, int count, float width_excess);

    // Widgets low-level behaviors
    SW_API bool                       ButtonBehavior(const SwRect& bb, SwGuiID id, bool* out_hovered, bool* out_held, SwGuiButtonFlags flags = 0);
    SW_API bool                       DragBehavior(SwGuiID id, SwGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, SwGuiSliderFlags flags);
    SW_API bool                       SliderBehavior(const SwRect& bb, SwGuiID id, SwGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, SwGuiSliderFlags flags, SwRect* out_grab_bb);
    SW_API bool                       SplitterBehavior(const SwRect& bb, SwGuiID id, SwGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, SwU32 bg_col = 0);

    // Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
   // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
   // e.g. " extern template SW_API float RoundScalarWithFormatT<float, float>(const char* format, SwGuiDataType data_type, float v); "
    template<typename T, typename SIGNED_T, typename FLOAT_T>   SW_API float ScaleRatioFromValueT(SwGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
    template<typename T, typename SIGNED_T, typename FLOAT_T>   SW_API T     ScaleValueFromRatioT(SwGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
    template<typename T, typename SIGNED_T, typename FLOAT_T>   SW_API bool  DragBehaviorT(SwGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, SwGuiSliderFlags flags);
    template<typename T, typename SIGNED_T, typename FLOAT_T>   SW_API bool  SliderBehaviorT(const SwRect& bb, SwGuiID id, SwGuiDataType data_type, T* v, T v_min, T v_max, const char* format, SwGuiSliderFlags flags, SwRect* out_grab_bb);
    template<typename T>                                        SW_API T     RoundScalarWithFormatT(const char* format, SwGuiDataType data_type, T v);
    template<typename T>                                        SW_API bool  CheckboxFlagsT(const char* label, T* flags, T flags_value);

    // Data type helpers
    SW_API const SwGuiDataTypeInfo*   DataTypeGetInfo(SwGuiDataType data_type);
    SW_API int                        DataTypeFormatString(char* buf, int buf_size, SwGuiDataType data_type, const void* p_data, const char* format);
    SW_API void                       DataTypeApplyOp(SwGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);
    SW_API bool                       DataTypeApplyFromText(const char* buf, SwGuiDataType data_type, void* p_data, const char* format);
    SW_API int                        DataTypeCompare(SwGuiDataType data_type, const void* arg_1, const void* arg_2);
    SW_API bool                       DataTypeClamp(SwGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);

    // InputText
    SW_API bool                       InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const SwVec2& size_arg, SwGuiInputTextFlags flags, SwGuiInputTextCallback callback = NULL, void* user_data = NULL);
    SW_API void                       InputTextDeactivateHook(SwGuiID id);
    SW_API bool                       TempInputText(const SwRect& bb, SwGuiID id, const char* label, char* buf, int buf_size, SwGuiInputTextFlags flags);
    SW_API bool                       TempInputScalar(const SwRect& bb, SwGuiID id, const char* label, SwGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);
    inline bool                       TempInputIsActive(SwGuiID id) { SwGuiContext& g = *GMUI_Ctx; return (g.ActiveId == id && g.TempInputId == id); }
    inline SwGuiInputTextState*       GetInputTextState(SwGuiID id) { SwGuiContext& g = *GMUI_Ctx; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active

    // Garbage collection
    SW_API void                       GcCompactTransientMiscBuffers();
    SW_API void                       GcCompactTransientWindowBuffers(SwGuiWindow* window);
    SW_API void                       GcAwakeTransientWindowBuffers(SwGuiWindow* window);

    // Texture Create Helper
    SW_API SwTexture                  CreateTextureImpl(const char* imagePath);
    SW_API SwTexture                  CreateGrayTextureImpl(const char* imagePath);

    // Drag and Drop
    SW_API bool                       IsDragDropActive();
    SW_API bool                       BeginDragDropTargetCustom(const SwRect& bb, SwGuiID id);
    SW_API void                       ClearDragDrop();
    SW_API bool                       IsDragDropPayloadBeingAccepted();
    SW_API void                       RenderDragDropTargetRect(const SwRect& bb);

    // Debug Log
    SW_API void                       DebugLog(const char* fmt, ...) SW_FMTARGS(1);
    SW_API void                       DebugLogV(const char* fmt, va_list args) SW_FMTLIST(1);

    // Debug Tools
    SW_API void                       DebugHookIdInfo(SwGuiID id, SwGuiDataType data_type, const void* data_id, const void* data_id_end);
    SW_API void                       ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
   
    // Docking
    // (some functions are only declared in imgui.cpp, see Docking section)
    SW_API void                       DockContextInitialize(SwGuiContext* ctx);
    SW_API void                       DockContextShutdown(SwGuiContext* ctx);
    SW_API void                       DockContextClearNodes(SwGuiContext* ctx, SwGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all
    SW_API void                       DockContextRebuildNodes(SwGuiContext* ctx);
    SW_API void                       DockContextNewFrameUpdateUndocking(SwGuiContext* ctx);
    SW_API void                       DockContextNewFrameUpdateDocking(SwGuiContext* ctx);
    SW_API void                       DockContextEndFrame(SwGuiContext* ctx);
    SW_API SwGuiID                    DockContextGenNodeID(SwGuiContext* ctx);
    SW_API void                       DockContextQueueDock(SwGuiContext* ctx, SwGuiWindow* target, SwGuiDockNode* target_node, SwGuiWindow* payload, SwGuiDir split_dir, float split_ratio, bool split_outer);
    SW_API void                       DockContextQueueUndockWindow(SwGuiContext* ctx, SwGuiWindow* window);
    SW_API void                       DockContextQueueUndockNode(SwGuiContext* ctx, SwGuiDockNode* node);
    SW_API bool                       DockContextCalcDropPosForDocking(SwGuiWindow* target, SwGuiDockNode* target_node, SwGuiWindow* payload_window, SwGuiDockNode* payload_node, SwGuiDir split_dir, bool split_outer, SwVec2* out_pos);
    SW_API SwGuiDockNode*             DockContextFindNodeByID(SwGuiContext* ctx, SwGuiID id);
    SW_API bool                       DockNodeBeginAmendTabBar(SwGuiDockNode* node);
    SW_API void                       DockNodeEndAmendTabBar();
    inline SwGuiDockNode*             DockNodeGetRootNode(SwGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; }
    inline bool                       DockNodeIsInHierarchyOf(SwGuiDockNode* node, SwGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; }
    inline int                        DockNodeGetDepth(const SwGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }
    inline SwGuiID                    DockNodeGetWindowMenuButtonId(const SwGuiDockNode* node) { return SwHashStr("#COLLAPSE", 0, node->ID); }
    inline SwGuiDockNode*             GetWindowDockNode() { SwGuiContext& g = *GMUI_Ctx; return g.CurrentWindow->DockNode; }
    SW_API bool                       GetWindowAlwaysWantOwnTabBar(SwGuiWindow* window);
    SW_API void                       BeginDocked(SwGuiWindow* window, bool* p_open);
    SW_API void                       BeginDockableDragDropSource(SwGuiWindow* window);
    SW_API void                       BeginDockableDragDropTarget(SwGuiWindow* window);
    SW_API void                       SetWindowDock(SwGuiWindow* window, SwGuiID dock_id, SwGuiCond cond);

    // Docking - Builder function needs to be generally called before the node is used/submitted.
    // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability.
    // - Do not hold on SwGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame.
    // - To create a DockSpace() node, make sure to set the SwGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode().
    //   You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API.
    // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node.
    // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure
    //   to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable.
    // - Call DockBuilderFinish() after you are done.
    SW_API void                       DockBuilderDockWindow(const char* window_name, SwGuiID node_id);
    SW_API SwGuiDockNode*             DockBuilderGetNode(SwGuiID node_id);
    inline SwGuiDockNode*             DockBuilderGetCentralNode(SwGuiID node_id) { SwGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; }
    SW_API SwGuiID                    DockBuilderAddNode(SwGuiID node_id = 0, SwGuiDockNodeFlags flags = 0);
    SW_API void                       DockBuilderRemoveNode(SwGuiID node_id);                 // Remove node and all its child, undock all windows
    SW_API void                       DockBuilderRemoveNodeDockedWindows(SwGuiID node_id, bool clear_settings_refs = true);
    SW_API void                       DockBuilderRemoveNodeChildNodes(SwGuiID node_id);       // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id).
    SW_API void                       DockBuilderSetNodePos(SwGuiID node_id, SwVec2 pos);
    SW_API void                       DockBuilderSetNodeSize(SwGuiID node_id, SwVec2 size);
    SW_API SwGuiID                    DockBuilderSplitNode(SwGuiID node_id, SwGuiDir split_dir, float size_ratio_for_node_at_dir, SwGuiID* out_id_at_dir, SwGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node.
    SW_API void                       DockBuilderCopyDockSpace(SwGuiID src_dockspace_id, SwGuiID dst_dockspace_id, SwVector<const char*>* in_window_remap_pairs);
    SW_API void                       DockBuilderCopyNode(SwGuiID src_node_id, SwGuiID dst_node_id, SwVector<SwGuiID>* out_node_remap_pairs);
    SW_API void                       DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name);
    SW_API void                       DockBuilderFinish(SwGuiID node_id);
}

//-----------------------------------------------------------------------------
// [SECTION] SwFontAtlas internal API
//-----------------------------------------------------------------------------

// This structure is likely to evolve as we add support for incremental atlas updates
struct SwFontBuilderIO
{
    bool    (*FontBuilder_Build)(SwFontAtlas* atlas);
};

// Helper for font builder
#ifdef SW_ENABLE_STB_TRUETYPE
SW_API const SwFontBuilderIO* SwFontAtlasGetBuilderForStbTruetype();
#endif
SW_API void      SwFontAtlasBuildInit(SwFontAtlas* atlas);
SW_API void      SwFontAtlasBuildSetupFont(SwFontAtlas* atlas, SwFont* font, SwFontConfig* font_config, float ascent, float descent);
SW_API void      SwFontAtlasBuildPackCustomRects(SwFontAtlas* atlas, void* stbrp_context_opaque);
SW_API void      SwFontAtlasBuildFinish(SwFontAtlas* atlas);
SW_API void      SwFontAtlasBuildRender8bppRectFromString(SwFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
SW_API void      SwFontAtlasBuildRender32bppRectFromString(SwFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value);
SW_API void      SwFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
SW_API void      SwFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);


//-----------------------------------------------------------------------------
// [SECTION] Test Engine specific hooks 
//-----------------------------------------------------------------------------

#ifdef SW_ENABLE_TEST_ENGINE
extern void         SwGuiTestEngineHook_ItemAdd(SwGuiContext* ctx, const SwRect& bb, SwGuiID id);
extern void         SwGuiTestEngineHook_ItemInfo(SwGuiContext* ctx, SwGuiID id, const char* label, SwGuiItemStatusFlags flags);
extern void         SwGuiTestEngineHook_Log(SwGuiContext* ctx, const char* fmt, ...);
extern const char*  SwGuiTestEngine_FindItemDebugLabel(SwGuiContext* ctx, SwGuiID id);

#define SW_TEST_ENGINE_ITEM_ADD(_BB,_ID)                 if (g.TestEngineHookItems) SwGuiTestEngineHook_ItemAdd(&g, _BB, _ID)               // Register item bounding box
#define SW_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      if (g.TestEngineHookItems) SwGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS)   // Register item label and status flags (optional)
#define SW_TEST_ENGINE_LOG(_FMT,...)                     if (g.TestEngineHookItems) SwGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)          // Custom log entry from user land into test log
#else
#define SW_TEST_ENGINE_ITEM_ADD(_BB,_ID)                 ((void)0)
#define SW_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      ((void)g)
#endif

//-----------------------------------------------------------------------------

#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

#ifdef _MSC_VER
#pragma warning (pop)
#endif


#endif